@forge/manifest

13.1.1-next.2-experimental-094961613.2.0-next.5
out/validators/modules-validators/rovo/skill-file-rules.js
+out/validators/modules-validators/rovo/skill-file-rules.jsNew file
+175
Index: package/out/validators/modules-validators/rovo/skill-file-rules.js
===================================================================
--- package/out/validators/modules-validators/rovo/skill-file-rules.js
+++ package/out/validators/modules-validators/rovo/skill-file-rules.js
@@ -0,0 +1,175 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.createAlwaysFailValidator = exports.buildReferencedFileValidators = exports.buildMetadataValueValidators = exports.buildUnknownFrontmatterFieldValidators = exports.skillValidationRules = exports.isRecord = exports.allowedFrontmatterFields = void 0;
+const text_1 = require("../../../text");
+const fs_1 = require("fs");
+const path_1 = require("path");
+exports.allowedFrontmatterFields = new Set([
+    'name',
+    'description',
+    'compatibility',
+    'metadata',
+    'license',
+    'allowed-tools'
+]);
+const nameRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
+const normalizeTools = (tools) => [...new Set(tools.map((tool) => tool.trim()).filter(Boolean))].sort();
+const parseTools = (allowedTools) => allowedTools
+    .split(' ')
+    .map((tool) => tool.trim())
+    .filter(Boolean);
+const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
+exports.isRecord = isRecord;
+exports.skillValidationRules = [
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => frontmatter.name !== undefined,
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileNameRequired(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => {
+            const name = frontmatter.name;
+            return name === undefined || (typeof name === 'string' && name.length >= 1 && name.length <= 64);
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileNameLength(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => {
+            const name = frontmatter.name;
+            return name === undefined || typeof name !== 'string' || nameRegex.test(name);
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileNameFormat(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter, parentDirectoryName }) => {
+            const name = frontmatter.name;
+            return name === undefined || typeof name !== 'string' || name === parentDirectoryName;
+        },
+        errorMessageGenerator: ({ skill: { key }, parentDirectoryName }) => text_1.errors.modules.rovo.skillFileNameMustMatchDirectory(key, parentDirectoryName)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => frontmatter.description !== undefined,
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileDescriptionRequired(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => {
+            const description = frontmatter.description;
+            return (description === undefined ||
+                (typeof description === 'string' && description.length >= 1 && description.length <= 1024));
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileDescriptionLength(key)
+    },
+    {
+        errorType: 'warning',
+        check: ({ frontmatter }) => {
+            const description = frontmatter.description;
+            return description === undefined || typeof description !== 'string' || description.length >= 50;
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileDescriptionTooShort(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => {
+            const compatibility = frontmatter.compatibility;
+            return compatibility === undefined || typeof compatibility === 'string';
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileCompatibilityMustBeString(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => {
+            const compatibility = frontmatter.compatibility;
+            return (compatibility === undefined ||
+                typeof compatibility !== 'string' ||
+                (compatibility.length >= 1 && compatibility.length <= 500));
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileCompatibilityLength(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ metadata }) => metadata === undefined || (0, exports.isRecord)(metadata),
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileMetadataInvalid(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ allowedTools }) => {
+            return allowedTools === undefined || (typeof allowedTools === 'string' && /^\S+(?: \S+)*$/.test(allowedTools));
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileAllowedToolsInvalid(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ allowedTools, dependencyTools }) => {
+            if (allowedTools === undefined || typeof allowedTools !== 'string' || !/^\S+(?: \S+)*$/.test(allowedTools)) {
+                return true;
+            }
+            const manifestTools = normalizeTools(dependencyTools);
+            const frontmatterTools = normalizeTools(parseTools(allowedTools));
+            const allowedToolsSet = new Set(frontmatterTools);
+            return manifestTools.every((tool) => allowedToolsSet.has(tool));
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileAllowedToolsMismatch(key)
+    },
+    {
+        errorType: 'error',
+        check: ({ frontmatter }) => {
+            const license = frontmatter.license;
+            return license === undefined || typeof license === 'string';
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileLicenseMustBeString(key)
+    },
+    {
+        errorType: 'warning',
+        check: ({ body }) => body.trim().length > 0,
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileBodyEmpty(key)
+    },
+    {
+        errorType: 'warning',
+        check: ({ body, bodyLines }) => body.trim().length === 0 || bodyLines.length <= 500,
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileBodyTooLong(key)
+    },
+    {
+        errorType: 'warning',
+        check: ({ body, dependencyTools }) => {
+            if (body.trim().length === 0) {
+                return true;
+            }
+            const tools = normalizeTools(dependencyTools);
+            return tools.every((tool) => new RegExp(`\\b${escapeRegExp(tool)}\\b`).test(body));
+        },
+        errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileToolsNotMentioned(key)
+    }
+];
+const buildUnknownFrontmatterFieldValidators = (context) => context.unknownFrontmatterFields.map((field) => ({
+    check: () => false,
+    errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileUnknownFrontmatterField(key, field),
+    errorType: 'error'
+}));
+exports.buildUnknownFrontmatterFieldValidators = buildUnknownFrontmatterFieldValidators;
+const buildMetadataValueValidators = (context) => context.metadataEntries.map((entry) => ({
+    check: () => typeof entry[1] === 'string',
+    errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileMetadataInvalid(key),
+    errorType: 'error'
+}));
+exports.buildMetadataValueValidators = buildMetadataValueValidators;
+const buildReferencedFileValidators = (context) => context.referencedBodyFiles.map((referencedBodyFile) => ({
+    check: ({ skillDirectory }) => {
+        const referencedFilePath = (0, path_1.resolve)(skillDirectory, referencedBodyFile);
+        return (0, fs_1.existsSync)(referencedFilePath) && (0, fs_1.statSync)(referencedFilePath).isFile();
+    },
+    errorMessageGenerator: ({ skill: { key } }) => text_1.errors.modules.rovo.skillFileReferencedFileMissing(key, referencedBodyFile),
+    errorType: 'warning'
+}));
+exports.buildReferencedFileValidators = buildReferencedFileValidators;
+const createAlwaysFailValidator = (errorType, errorMessageGenerator) => ({
+    check: () => false,
+    errorMessageGenerator: () => errorMessageGenerator(),
+    errorType
+});
+exports.createAlwaysFailValidator = createAlwaysFailValidator;