Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207const std = @import("std");const io_mod = @import("../shared/io.zig");const profile_paths = @import("../shared/profile_paths.zig");const skill_contract = @import("../skills/skill_contract.zig");const skill_runtime = @import("../skills/skill_runtime.zig");
const Allocator = std.mem.Allocator;pub const LoadSkillsError = Allocator.Error;
pub const LoadedSkills = struct { dir: []u8 = &.{}, skills: []skill_runtime.Skill = &.{}, diagnostics: []skill_runtime.SkillDiagnostic = &.{},
pub fn deinit(self: *LoadedSkills, alloc: Allocator) void { if (self.dir.len > 0) alloc.free(self.dir); skill_runtime.freeSkills(alloc, self.skills); skill_runtime.freeSkillDiagnostics(alloc, self.diagnostics); self.* = .{}; }};
pub fn resolveSkillsHome(alloc: Allocator) Allocator.Error!?[]u8 { const configured_home = io_mod.getenv("HOME") orelse return null; return io_mod.realpathAlloc(alloc, configured_home) catch |err| switch (err) { error.OutOfMemory => error.OutOfMemory, else => try alloc.dupe(u8, configured_home), };}
pub fn loadSkills( alloc: Allocator, workspace_root: []const u8, root_policy: skill_contract.RootPolicy,) LoadSkillsError!LoadedSkills { const home = (try resolveSkillsHome(alloc)) orelse return .{}; defer alloc.free(home); const dir = try profile_paths.managedSkillsDir(alloc, home); errdefer alloc.free(dir); const discovery = try skill_runtime.loadVisibleSkills(alloc, workspace_root, home, dir, root_policy);
return .{ .dir = dir, .skills = discovery.skills, .diagnostics = discovery.diagnostics, };}
var stable_test_environ: ?*std.process.Environ.Map = null;
fn stableEmptyTestEnviron() !*const std.process.Environ.Map { if (stable_test_environ) |map| return map;
const alloc = std.heap.page_allocator; const map = try alloc.create(std.process.Environ.Map); map.* = std.process.Environ.Map.init(alloc); stable_test_environ = map; return map;}
const TestHome = struct { alloc: Allocator, map: std.process.Environ.Map,
fn install(alloc: Allocator, home: ?[]const u8) !*TestHome { _ = try stableEmptyTestEnviron();
const self = try alloc.create(TestHome); errdefer alloc.destroy(self);
self.* = .{ .alloc = alloc, .map = std.process.Environ.Map.init(alloc), }; errdefer self.map.deinit();
if (home) |value| { try self.map.put("HOME", value); }
io_mod.setEnvironMap(&self.map); return self; }
fn deinit(self: *TestHome) void { if (stable_test_environ) |map| { io_mod.setEnvironMap(map); } self.map.deinit(); const alloc = self.alloc; alloc.destroy(self); }};
fn tmpPath(alloc: Allocator, dir: std.Io.Dir, sub_path: []const u8) ![]u8 { return io_mod.dirRealpathAlloc(alloc, dir, sub_path);}
fn writeTempFile(tmp: *std.testing.TmpDir, sub_path: []const u8, content: []const u8) !void { if (std.fs.path.dirname(sub_path)) |parent| { try tmp.dir.createDirPath(io_mod.getIo(), parent); } var file = try tmp.dir.createFile(io_mod.getIo(), sub_path, .{ .truncate = true }); defer file.close(io_mod.getIo()); try file.writeStreamingAll(io_mod.getIo(), content);}
const test_root_policy: skill_contract.RootPolicy = .{ .managed_root_source = .global_fx,};
test "loadSkills returns empty defaults when HOME is missing" { const alloc = std.testing.allocator; const home = try TestHome.install(alloc, null); defer home.deinit();
var loaded = try loadSkills(alloc, "/tmp/workspace", test_root_policy); defer loaded.deinit(alloc);
try std.testing.expectEqual(@as(usize, 0), loaded.dir.len); try std.testing.expectEqual(@as(usize, 0), loaded.skills.len);}
test "loadSkills loads managed skills under HOME" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup();
try writeTempFile(&tmp, "home/.fx/skills/demo/SKILL.md", \\--- \\name: demo \\description: Demo skill \\--- \\Use this for tests. ); try tmp.dir.createDirPath(io_mod.getIo(), "home/workspace");
const home_path = try tmpPath(alloc, tmp.dir, "home"); defer alloc.free(home_path); const workspace_path = try tmpPath(alloc, tmp.dir, "home/workspace"); defer alloc.free(workspace_path);
const home = try TestHome.install(alloc, home_path); defer home.deinit();
var loaded = try loadSkills(alloc, workspace_path, test_root_policy); defer loaded.deinit(alloc);
const expected_dir = try profile_paths.managedSkillsDir(alloc, home_path); defer alloc.free(expected_dir);
try std.testing.expectEqualStrings(expected_dir, loaded.dir); try std.testing.expectEqual(@as(usize, 1), loaded.skills.len); try std.testing.expectEqualStrings("demo", loaded.skills[0].name); try std.testing.expectEqualStrings("Demo skill", loaded.skills[0].description); try std.testing.expectEqual(skill_runtime.SkillSource.global_fx, loaded.skills[0].source); try std.testing.expectEqual(@as(usize, 0), loaded.diagnostics.len);}
test "loadSkills canonicalizes a symlinked HOME before discovering optional roots" { if (comptime @import("builtin").os.tag == .windows) return error.SkipZigTest;
const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup();
try tmp.dir.createDirPath(io_mod.getIo(), "real-home/workspace"); tmp.dir.symLink(std.testing.io, "real-home", "linked-home", .{ .is_directory = true }) catch |err| { if (err == error.AccessDenied or err == error.FileSystem) return error.SkipZigTest; return err; }; const tmp_root = try tmpPath(alloc, tmp.dir, "."); defer alloc.free(tmp_root); const linked_home = try std.fs.path.join(alloc, &.{ tmp_root, "linked-home" }); defer alloc.free(linked_home); const workspace_path = try tmpPath(alloc, tmp.dir, "real-home/workspace"); defer alloc.free(workspace_path);
const home = try TestHome.install(alloc, linked_home); defer home.deinit();
var loaded = try loadSkills(alloc, workspace_path, test_root_policy); defer loaded.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), loaded.skills.len); try std.testing.expectEqual(@as(usize, 0), loaded.diagnostics.len);}
test "loadSkills propagates allocation failure instead of returning an empty inventory" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.createDirPath(io_mod.getIo(), "home/workspace");
const home_path = try tmpPath(alloc, tmp.dir, "home"); defer alloc.free(home_path); const workspace_path = try tmpPath(alloc, tmp.dir, "home/workspace"); defer alloc.free(workspace_path); const home = try TestHome.install(alloc, home_path); defer home.deinit();
var failing = std.testing.FailingAllocator.init(alloc, .{ .fail_index = 0 }); try std.testing.expectError( error.OutOfMemory, loadSkills(failing.allocator(), workspace_path, test_root_policy), );}