feat: integrate performance upgrade and agent inspection tooling
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ShrinkSDK.Inspect;
|
||||
|
||||
public static class ModuleCatalog
|
||||
{
|
||||
private static readonly HashSet<string> Excluded = new(StringComparer.OrdinalIgnoreCase)
|
||||
{ ".git", "Library", "Temp", "obj", "bin", "Logs", "Builds", "Artifacts", "node_modules", "Development~", "Samples~" };
|
||||
|
||||
public static IEnumerable<string> Files(string root, string pattern)
|
||||
{
|
||||
foreach (var file in Directory.EnumerateFiles(root, pattern)) yield return file;
|
||||
foreach (var dir in Directory.EnumerateDirectories(root))
|
||||
{
|
||||
if (Excluded.Contains(Path.GetFileName(dir)) || (File.GetAttributes(dir) & FileAttributes.ReparsePoint) != 0) continue;
|
||||
foreach (var file in Files(dir, pattern)) yield return file;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Load(Index index)
|
||||
{
|
||||
var installed = Path.Combine(index.Root, "Library", "PackageCache");
|
||||
var scanRoots = new List<string> { index.Root };
|
||||
if (Directory.Exists(installed)) scanRoots.AddRange(Directory.GetDirectories(installed, "com.cneicy.shrink-*@*"));
|
||||
var packages = scanRoots.SelectMany(r => Files(r, "package.json")).Select(path => (path, doc: Read(path)))
|
||||
.Where(x => x.doc.TryGetProperty("name", out _)).ToArray();
|
||||
var asmdefs = scanRoots.SelectMany(r => Files(r, "*.asmdef")).ToArray();
|
||||
var roots = packages.Select(p => Path.GetDirectoryName(p.path)!).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var path in asmdefs)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(path)!;
|
||||
if (!roots.Any(root => dir.Equals(root, StringComparison.OrdinalIgnoreCase) || dir.StartsWith(root + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))) roots.Add(dir);
|
||||
}
|
||||
// Business directories can share an asmdef while retaining separate ownership.
|
||||
foreach (var readme in scanRoots.SelectMany(r => Files(r, "README.md")))
|
||||
if (File.ReadAllText(readme).Contains("<!-- shrink:capabilities -->", StringComparison.Ordinal)) roots.Add(Path.GetDirectoryName(readme)!);
|
||||
|
||||
foreach (var root in roots.Order(StringComparer.Ordinal))
|
||||
{
|
||||
var package = packages.FirstOrDefault(p => Path.GetDirectoryName(p.path) == root);
|
||||
var name = package.doc.ValueKind == JsonValueKind.Object ? String(package.doc, "name") : Path.GetFileName(root);
|
||||
var version = package.doc.ValueKind == JsonValueKind.Object ? String(package.doc, "version") : "unversioned";
|
||||
var dependencies = package.doc.ValueKind == JsonValueKind.Object && package.doc.TryGetProperty("dependencies", out var deps)
|
||||
? deps.EnumerateObject().Select(p => p.Name + "@" + p.Value.GetString()).ToArray() : [];
|
||||
var assemblies = asmdefs.Where(p => p.StartsWith(root + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(p => String(Read(p), "name")).ToArray();
|
||||
var readme = Path.Combine(root, "README.md");
|
||||
var complete = File.Exists(readme) && ReadCapabilities(name, readme, index.Capabilities);
|
||||
var validation = Files(root, "*.cs").Where(p => p.Contains("Tests", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(p => Path.GetRelativePath(index.Root, p)).Take(8).ToArray();
|
||||
index.Modules.Add(new(name, root, version, dependencies, assemblies, validation, complete));
|
||||
}
|
||||
foreach (var module in index.Modules)
|
||||
foreach (var dep in module.Dependencies)
|
||||
{
|
||||
var split = dep.LastIndexOf('@');
|
||||
if (split <= 0) continue;
|
||||
var target = index.Modules.FirstOrDefault(m => m.Name == dep[..split]);
|
||||
if (target == null) continue;
|
||||
if (target.Version != dep[(split + 1)..]) index.Findings.Add(new("SHRINK101", "error", $"{module.Name} requires {dep}, workspace has {target.Version}", new(Path.Combine(module.Root, "package.json"), 1)));
|
||||
if (!module.Name.Contains("integration") && !module.Name.Contains("starter") && target.Name.Contains("-integration-"))
|
||||
index.Findings.Add(new("SHRINK102", "error", $"Main package {module.Name} depends on integration {target.Name}.", new(Path.Combine(module.Root, "package.json"), 1)));
|
||||
}
|
||||
foreach (var module in index.Modules)
|
||||
Visit(module, new HashSet<string>(), new HashSet<string>());
|
||||
void Visit(ModuleInfo current, HashSet<string> path, HashSet<string> seen)
|
||||
{
|
||||
if (!path.Add(current.Name)) { index.Findings.Add(new("SHRINK103", "error", "Package dependency cycle: " + string.Join(" -> ", path) + " -> " + current.Name, new(Path.Combine(current.Root, "package.json"), 1))); return; }
|
||||
if (seen.Add(current.Name))
|
||||
foreach (var dep in current.Dependencies)
|
||||
{
|
||||
var target = index.Modules.FirstOrDefault(m => dep.StartsWith(m.Name + "@", StringComparison.Ordinal));
|
||||
if (target != null) Visit(target, path, seen);
|
||||
}
|
||||
path.Remove(current.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ReadCapabilities(string module, string path, List<Capability> output)
|
||||
{
|
||||
var lines = File.ReadAllLines(path);
|
||||
var start = Array.IndexOf(lines, "<!-- shrink:capabilities -->");
|
||||
var end = Array.IndexOf(lines, "<!-- /shrink:capabilities -->");
|
||||
if (start < 0 || end <= start) return false;
|
||||
string Field(string key) => lines.Skip(start + 1).Take(end - start - 1)
|
||||
.FirstOrDefault(s => s.StartsWith(key + ":", StringComparison.Ordinal))?[(key.Length + 1)..].Trim() ?? "";
|
||||
output.Add(new(module, Field("Capability"), Field("Aliases"), Field("Limits"), Field("Extension"), Field("Evidence"), new(path, start + 1)));
|
||||
return new[] { "Capability", "Limits", "Extension", "Evidence" }.All(key => Field(key).Length > 0);
|
||||
}
|
||||
|
||||
public static IEnumerable<Capability> Search(Index index, string query, string? module)
|
||||
{
|
||||
var terms = Regex.Split(query.Trim(), @"[\s,,;;/]+").Where(t => t.Length > 0).ToArray();
|
||||
return index.Capabilities.Where(c => module == null || c.Module.Contains(module, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(c => {
|
||||
var text = c.Module + " " + c.Summary + " " + c.Aliases;
|
||||
var matches = terms.Where(t => text.Contains(t, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
return c with { Score = matches.Length, MatchReason = string.Join(", ", matches) };
|
||||
}).Where(c => terms.Length == 0 || c.Score > 0).OrderByDescending(c => c.Score).ThenBy(c => c.Module, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
private static JsonElement Read(string path) { using var doc = JsonDocument.Parse(File.ReadAllText(path)); return doc.RootElement.Clone(); }
|
||||
private static string String(JsonElement e, string name) => e.TryGetProperty(name, out var p) ? p.GetString() ?? "" : "";
|
||||
}
|
||||
Reference in New Issue
Block a user