mirror of
https://github.com/supabase/agent-skills.git
synced 2026-03-27 10:09:26 +08:00
103 lines
2.6 KiB
TypeScript
103 lines
2.6 KiB
TypeScript
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import type { Profile, Rule } from "./types.js";
|
|
|
|
/**
|
|
* Load a profile from the profiles directory
|
|
*/
|
|
export function loadProfile(profilesDir: string, profileName: string): Profile | null {
|
|
const profileFile = join(profilesDir, `${profileName}.json`);
|
|
if (!existsSync(profileFile)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return JSON.parse(readFileSync(profileFile, "utf-8"));
|
|
} catch (error) {
|
|
console.error(`Error loading profile ${profileName}:`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List all available profiles in the profiles directory
|
|
*/
|
|
export function listProfiles(profilesDir: string): string[] {
|
|
if (!existsSync(profilesDir)) {
|
|
return [];
|
|
}
|
|
|
|
return readdirSync(profilesDir)
|
|
.filter((f) => f.endsWith(".json"))
|
|
.map((f) => f.replace(".json", ""));
|
|
}
|
|
|
|
/**
|
|
* Compare version strings (e.g., "9.5", "11", "14.2")
|
|
* Returns: negative if a < b, 0 if equal, positive if a > b
|
|
*/
|
|
function compareVersions(a: string, b: string): number {
|
|
const partsA = a.split(".").map(Number);
|
|
const partsB = b.split(".").map(Number);
|
|
|
|
const maxLen = Math.max(partsA.length, partsB.length);
|
|
for (let i = 0; i < maxLen; i++) {
|
|
const numA = partsA[i] || 0;
|
|
const numB = partsB[i] || 0;
|
|
if (numA !== numB) {
|
|
return numA - numB;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Check if a rule is compatible with a profile
|
|
*/
|
|
export function isRuleCompatibleWithProfile(rule: Rule, profile: Profile): boolean {
|
|
// Check version requirement
|
|
if (rule.minVersion) {
|
|
if (compareVersions(rule.minVersion, profile.minVersion) > 0) {
|
|
// Rule requires a higher version than profile supports
|
|
return false;
|
|
}
|
|
if (profile.maxVersion && compareVersions(rule.minVersion, profile.maxVersion) > 0) {
|
|
// Rule requires a version higher than profile's max
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check extension requirements
|
|
if (rule.extensions && rule.extensions.length > 0) {
|
|
const allExtensions = [
|
|
...(profile.extensions.available || []),
|
|
...(profile.extensions.installable || []),
|
|
];
|
|
|
|
for (const ext of rule.extensions) {
|
|
if (profile.extensions.unavailable?.includes(ext)) {
|
|
// Extension is explicitly unavailable in this profile
|
|
return false;
|
|
}
|
|
if (!allExtensions.includes(ext)) {
|
|
// Extension is not available or installable
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if rule is explicitly excluded
|
|
if (profile.excludeRules?.includes(rule.id)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Filter rules based on profile constraints
|
|
*/
|
|
export function filterRulesForProfile(rules: Rule[], profile: Profile): Rule[] {
|
|
return rules.filter((rule) => isRuleCompatibleWithProfile(rule, profile));
|
|
}
|