@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;