@mastra/core
Version:
388 lines (387 loc) • 12.2 kB
JavaScript
const require_rolldown_runtime = require("./rolldown-runtime-uwYp4b74.cjs");
const require_workspace_skills = require("./workspace-skills-CjnfyQuP.cjs");
let gray_matter = require("gray-matter");
gray_matter = require_rolldown_runtime.__toESM(gray_matter, 1);
//#region src/skills/create-skill.ts
/**
* createSkill() — factory for creating inline skills in code.
*
* Creates a Skill object that can be passed to an Agent's `skills` config
* without requiring a Workspace or filesystem.
*
* @example
* ```typescript
* import { createSkill } from '@mastra/core/skills';
*
* const reviewSkill = createSkill({
* name: 'code-review',
* description: 'Use when reviewing code changes.',
* instructions: `
* When reviewing code:
* 1. Check for correctness
* 2. Check for style consistency
* 3. Look for potential bugs
* `,
* references: {
* 'checklist.md': '# Review Checklist\n...',
* },
* });
* ```
*/
/**
* Create an inline skill from code — no filesystem needed.
*
* The returned object implements the `Skill` interface and can be passed
* directly to an Agent's `skills` config or used anywhere a `Skill` is expected.
*
* @throws Error if the skill metadata fails validation
*/
function createSkill(input) {
const { name, description, instructions, license, compatibility, metadata, references } = input;
const validation = require_workspace_skills.validateSkillMetadata({
name,
description,
license,
compatibility,
"user-invocable": input["user-invocable"],
metadata
}, void 0, instructions);
if (!validation.valid) throw new Error(`Invalid skill "${name}": ${validation.errors.join("; ")}`);
const referenceKeys = references ? Object.keys(references) : [];
return {
__inline: true,
__referenceContents: references ?? {},
name,
description,
instructions,
license,
compatibility,
"user-invocable": input["user-invocable"],
metadata,
path: `inline/${name}`,
source: {
type: "local",
projectPath: `inline/${name}`
},
references: referenceKeys,
scripts: [],
assets: []
};
}
/**
* Type guard: is this skill an inline skill (from createSkill)?
*/
function isInlineSkill(skill) {
return typeof skill === "object" && skill !== null && "__inline" in skill && skill.__inline === true;
}
//#endregion
//#region src/skills/inline-skill-source.ts
/**
* InlineSkillSource — in-memory SkillSource for code-defined skills.
*
* Serves skills created via `createSkill()` without any filesystem dependency.
* Implements the SkillSource interface so it can be used with WorkspaceSkillsImpl.
*
* Directory layout emulation:
* Each inline skill appears as a directory at `inline/<name>/` with:
* - SKILL.md (generated from the skill's metadata + instructions)
* - references/<file> (from the skill's `references` map)
*/
var InlineSkillSource = class {
#skills;
/** Pre-built SKILL.md content per skill name */
#skillMdCache;
constructor(skills) {
this.#skills = new Map(skills.map((s) => [s.name, s]));
this.#skillMdCache = /* @__PURE__ */ new Map();
for (const skill of skills) this.#skillMdCache.set(skill.name, this.#buildSkillMd(skill));
}
/**
* Build a synthetic SKILL.md from an inline skill's metadata and instructions.
*/
#buildSkillMd(skill) {
const frontmatter = {
name: skill.name,
description: skill.description
};
if (skill.license) frontmatter.license = skill.license;
if (skill.compatibility) frontmatter.compatibility = skill.compatibility;
if (skill["user-invocable"] !== void 0) frontmatter["user-invocable"] = skill["user-invocable"];
if (skill.metadata) frontmatter.metadata = skill.metadata;
return gray_matter.default.stringify(skill.instructions, frontmatter);
}
/**
* Parse a path into skill name and relative sub-path.
* Paths look like `inline/<name>`, `inline/<name>/SKILL.md`, `inline/<name>/references/file.md`
*/
#parsePath(inputPath) {
if (!inputPath.startsWith("inline/")) return null;
const rest = inputPath.slice(7);
const slashIdx = rest.indexOf("/");
if (slashIdx === -1) return {
skillName: rest,
subPath: ""
};
return {
skillName: rest.slice(0, slashIdx),
subPath: rest.slice(slashIdx + 1)
};
}
#getSkill(inputPath) {
const parsed = this.#parsePath(inputPath);
if (!parsed) return null;
const skill = this.#skills.get(parsed.skillName);
if (!skill) return null;
return {
skill,
subPath: parsed.subPath
};
}
async exists(path) {
const result = this.#getSkill(path);
if (!result) return false;
const { skill, subPath } = result;
if (subPath === "") return true;
if (subPath === "SKILL.md") return true;
if (subPath === "references") return (skill.references?.length ?? 0) > 0;
if (subPath.startsWith("references/")) {
const refPath = subPath.slice(11);
return skill.references.includes(refPath);
}
return false;
}
async stat(path) {
const result = this.#getSkill(path);
if (!result) throw new Error(`ENOENT: no such file or directory: ${path}`);
const { skill, subPath } = result;
const now = /* @__PURE__ */ new Date();
if (subPath === "") return {
name: skill.name,
type: "directory",
size: 0,
createdAt: now,
modifiedAt: now
};
if (subPath === "SKILL.md") {
const content = this.#skillMdCache.get(skill.name) ?? "";
return {
name: "SKILL.md",
type: "file",
size: Buffer.byteLength(content, "utf-8"),
createdAt: now,
modifiedAt: now,
mimeType: "text/markdown"
};
}
if (subPath === "references") return {
name: "references",
type: "directory",
size: 0,
createdAt: now,
modifiedAt: now
};
throw new Error(`ENOENT: no such file or directory: ${path}`);
}
async readFile(path) {
const result = this.#getSkill(path);
if (!result) throw new Error(`ENOENT: no such file or directory: ${path}`);
const { skill, subPath } = result;
if (subPath === "SKILL.md") return this.#skillMdCache.get(skill.name) ?? "";
if (subPath.startsWith("references/")) {
const refPath = subPath.slice(11);
const content = skill.__referenceContents[refPath];
if (content !== void 0) return content;
}
throw new Error(`ENOENT: no such file or directory: ${path}`);
}
async readdir(path) {
const parsed = this.#parsePath(path);
if (!parsed) return [];
const { skillName, subPath } = parsed;
const skill = this.#skills.get(skillName);
if (!skill) return [];
if (subPath === "") {
const entries = [{
name: "SKILL.md",
type: "file"
}];
if (skill.references.length > 0) entries.push({
name: "references",
type: "directory"
});
return entries;
}
if (subPath === "references") return skill.references.map((ref) => ({
name: ref,
type: "file"
}));
return [];
}
};
//#endregion
//#region src/skills/agent-skills-resolver.ts
/**
* Combines multiple SkillSources, routing by path prefix.
* Inline skills use `inline:<name>` paths; everything else goes to LocalSkillSource.
*/
var CompositeSkillSource = class {
#local;
#inline;
constructor(local, inline) {
this.#local = local;
this.#inline = inline;
}
#route(path) {
return path.startsWith("inline/") ? this.#inline : this.#local;
}
exists(path) {
return this.#route(path).exists(path);
}
stat(path) {
return this.#route(path).stat(path);
}
readFile(path) {
return this.#route(path).readFile(path);
}
readdir(path) {
return this.#route(path).readdir(path);
}
async realpath(path) {
const source = this.#route(path);
return source.realpath ? source.realpath(path) : path;
}
};
/**
* Resolve an array of SkillInput items into a WorkspaceSkills instance.
*
* @param skills - Array of path strings and/or inline skills
* @returns A WorkspaceSkills implementation ready for use by the Agent
*/
function resolveAgentSkills(skills) {
const inlineSkills = [];
const pathSkills = [];
for (const skill of skills) if (isInlineSkill(skill)) inlineSkills.push(skill);
else pathSkills.push(skill);
let source;
const skillPaths = [...pathSkills];
if (inlineSkills.length > 0 && pathSkills.length > 0) {
source = new CompositeSkillSource(new require_workspace_skills.LocalSkillSource(), new InlineSkillSource(inlineSkills));
for (const skill of inlineSkills) skillPaths.push(`inline/${skill.name}`);
} else if (inlineSkills.length > 0) {
source = new InlineSkillSource(inlineSkills);
for (const skill of inlineSkills) skillPaths.push(`inline/${skill.name}`);
} else source = new require_workspace_skills.LocalSkillSource();
return new require_workspace_skills.WorkspaceSkillsImpl({
source,
skills: skillPaths,
validateOnLoad: true
});
}
/**
* Merge two WorkspaceSkills instances by combining their skill lists.
* Agent-level skills take precedence on name conflicts (returned first in list).
*/
async function mergeWorkspaceSkills(agentSkills, workspaceSkills) {
return {
merged: new MergedWorkspaceSkills(agentSkills, workspaceSkills),
agentSkillNames: new Set((await agentSkills.list()).map((s) => s.name))
};
}
/**
* A WorkspaceSkills wrapper that merges two skill sets.
* Agent skills take precedence on name conflicts.
*/
var MergedWorkspaceSkills = class {
#primary;
#secondary;
constructor(primary, secondary) {
this.#primary = primary;
this.#secondary = secondary;
}
async list() {
const primaryList = await this.#primary.list();
const secondaryList = await this.#secondary.list();
const primaryNames = new Set(primaryList.map((s) => s.name));
return [...primaryList, ...secondaryList.filter((s) => !primaryNames.has(s.name))];
}
async get(name) {
const primary = await this.#primary.get(name);
if (primary) return primary;
return this.#secondary.get(name);
}
async has(name) {
return await this.#primary.has(name) || await this.#secondary.has(name);
}
async refresh() {
await Promise.all([this.#primary.refresh(), this.#secondary.refresh()]);
}
async maybeRefresh(context) {
await Promise.all([this.#primary.maybeRefresh(context), this.#secondary.maybeRefresh(context)]);
}
async search(query, options) {
const [primaryResults, secondaryResults] = await Promise.all([this.#primary.search(query, options), this.#secondary.search(query, options)]);
return [...primaryResults, ...secondaryResults].sort((a, b) => b.score - a.score);
}
async getReference(skillName, referencePath) {
const primary = await this.#primary.getReference(skillName, referencePath);
if (primary !== null) return primary;
return this.#secondary.getReference(skillName, referencePath);
}
async getScript(skillName, scriptPath) {
const primary = await this.#primary.getScript(skillName, scriptPath);
if (primary !== null) return primary;
return this.#secondary.getScript(skillName, scriptPath);
}
async getAsset(skillName, assetPath) {
const primary = await this.#primary.getAsset(skillName, assetPath);
if (primary !== null) return primary;
return this.#secondary.getAsset(skillName, assetPath);
}
async listReferences(skillName) {
const primary = await this.#primary.listReferences(skillName);
if (primary.length > 0) return primary;
return this.#secondary.listReferences(skillName);
}
async listScripts(skillName) {
const primary = await this.#primary.listScripts(skillName);
if (primary.length > 0) return primary;
return this.#secondary.listScripts(skillName);
}
async listAssets(skillName) {
const primary = await this.#primary.listAssets(skillName);
if (primary.length > 0) return primary;
return this.#secondary.listAssets(skillName);
}
};
//#endregion
Object.defineProperty(exports, "InlineSkillSource", {
enumerable: true,
get: function() {
return InlineSkillSource;
}
});
Object.defineProperty(exports, "createSkill", {
enumerable: true,
get: function() {
return createSkill;
}
});
Object.defineProperty(exports, "isInlineSkill", {
enumerable: true,
get: function() {
return isInlineSkill;
}
});
Object.defineProperty(exports, "mergeWorkspaceSkills", {
enumerable: true,
get: function() {
return mergeWorkspaceSkills;
}
});
Object.defineProperty(exports, "resolveAgentSkills", {
enumerable: true,
get: function() {
return resolveAgentSkills;
}
});
//# sourceMappingURL=agent-skills-resolver-BfwRVTvX.cjs.map