diff --git a/docs/release-notes.md b/docs/release-notes.md
index 5cdc539b..36568e09 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -5,6 +5,9 @@
* For players:
* Fixed `log_context` command not disabling the extra logs when run again.
+* For external tools:
+ * Added toolkit method to read the compatibility list from a local copy of its Git repo.
+
## 4.1.10
Released 18 December 2024 for Stardew Valley 1.6.14 or later.
diff --git a/src/SMAPI.Toolkit/Framework/Clients/CompatibilityRepo/CompatibilityRepoClient.cs b/src/SMAPI.Toolkit/Framework/Clients/CompatibilityRepo/CompatibilityRepoClient.cs
index e25d890a..ba889910 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/CompatibilityRepo/CompatibilityRepoClient.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/CompatibilityRepo/CompatibilityRepoClient.cs
@@ -1,7 +1,9 @@
using System;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Markdig;
+using Newtonsoft.Json;
using Pathoschild.Http.Client;
using StardewModdingAPI.Toolkit.Framework.Clients.CompatibilityRepo.Internal;
using StardewModdingAPI.Toolkit.Framework.Clients.CompatibilityRepo.RawDataModels;
@@ -53,6 +55,25 @@ public class CompatibilityRepoClient : IDisposable
.ToArray();
}
+ /// Fetch mods from the compatibility list by reading a local copy of the compatibility list repo.
+ /// The full path to the compatibility list repo folder.
+ public async Task FetchModsFromLocalGitFolderAsync(string gitRepoPath)
+ {
+ string modsJsonPath = Path.Combine(gitRepoPath, "data", "mods.jsonc");
+ string contentPacksJsonPath = Path.Combine(gitRepoPath, "data", "broken-content-packs.jsonc");
+ if (!File.Exists(modsJsonPath) || !File.Exists(contentPacksJsonPath))
+ throw new InvalidOperationException("The compatibility list repo folder doesn't contain the required JSON files.");
+
+ RawCompatibilityList? mods = JsonConvert.DeserializeObject(File.ReadAllText(modsJsonPath));
+ RawCompatibilityList? brokenContentPacks = JsonConvert.DeserializeObject(File.ReadAllText(contentPacksJsonPath));
+
+ return
+ (mods?.Mods ?? Array.Empty())
+ .Concat(brokenContentPacks?.BrokenContentPacks ?? Array.Empty())
+ .Select(this.ParseRawModEntry)
+ .ToArray();
+ }
+
/// Get the inline HTML produced by a Markdown string in a compatibility repo field.
/// The Markdown to parse.
/// This is a low-level method. Most code should use instead.
diff --git a/src/SMAPI.Toolkit/ModToolkit.cs b/src/SMAPI.Toolkit/ModToolkit.cs
index b0284966..b7117a67 100644
--- a/src/SMAPI.Toolkit/ModToolkit.cs
+++ b/src/SMAPI.Toolkit/ModToolkit.cs
@@ -70,6 +70,14 @@ public class ModToolkit
return await client.FetchModsAsync();
}
+ /// Extract mod metadata from the compatibility list repo by reading a local copy of the repo.
+ /// The full path to the compatibility list repo folder.
+ public async Task GetCompatibilityListFromLocalGitFolderAsync(string gitRepoPath)
+ {
+ using CompatibilityRepoClient client = new(this.UserAgent);
+ return await client.FetchModsFromLocalGitFolderAsync(gitRepoPath);
+ }
+
/// Get SMAPI's internal mod database.
/// The file path for the SMAPI metadata file.
public ModDatabase GetModDatabase(string metadataPath)