@cuylabs/agent-capability-pack 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pack/types.ts","../src/sources/validate.ts","../src/sources/inline-source.ts","../src/sources/local-source.ts","../src/sources/npm-source.ts","../src/sources/url-source.ts","../src/sources/git-source.ts","../src/sources/resolve.ts"],"sourcesContent":["/**\n * Capability Pack — core types.\n *\n * A CapabilityPack is the distributable unit of agent capabilities.\n * It bundles any combination of skills, MCP server configurations,\n * and plugin contributions into a single, composable object.\n *\n * Packs are source-agnostic by design: whether a pack was authored\n * inline, loaded from an npm package, fetched from a URL, or read\n * from a local directory, it always resolves to this same shape.\n * The PackLoader feeds it into the agent-core subsystems.\n *\n * @packageDocumentation\n */\n\nimport type { MCPServerConfig } from \"@cuylabs/agent-core/mcp\";\nimport type { PluginDefinition, PluginInit } from \"@cuylabs/agent-core/plugin\";\n\n/** Marker value for JSON-backed pack manifests. */\nexport const CAPABILITY_PACK_KIND = \"agent-capability-pack\";\n\n/** Current JSON manifest schema version for capability packs. */\nexport const CAPABILITY_PACK_SCHEMA_VERSION = 1;\n\n// ============================================================================\n// Pack metadata\n// ============================================================================\n\n/** Author information for a capability pack. */\nexport interface PackAuthor {\n /** Display name of the author or organization. */\n name: string;\n /** Author website or GitHub profile URL. */\n url?: string;\n /** Contact email. */\n email?: string;\n}\n\n/**\n * Identifying metadata for a capability pack.\n *\n * These fields are shared by both `CapabilityPack` and pack manifest\n * files on disk (`pack.json`).\n */\nexport interface PackMeta {\n /** Optional manifest kind marker for JSON-backed packs. */\n kind?: typeof CAPABILITY_PACK_KIND;\n /** Optional manifest schema version for JSON-backed packs. */\n schemaVersion?: typeof CAPABILITY_PACK_SCHEMA_VERSION;\n /**\n * Unique pack identifier. Used for namespacing, deduplication, and logging.\n * Should be kebab-case (e.g. \"my-company-tools\").\n */\n id: string;\n /** Human-readable display name. */\n name?: string;\n /** Brief description of what this pack provides. */\n description?: string;\n /** SemVer version string. */\n version?: string;\n /** Author or owning organization. */\n author?: PackAuthor;\n}\n\n// ============================================================================\n// Pack skills\n// ============================================================================\n\n/** Resource type classification — mirrors `SkillResourceType` from agent-core. */\nexport type PackSkillResourceType =\n | \"script\"\n | \"reference\"\n | \"asset\"\n | \"example\";\n\n/**\n * A bundled resource included with a pack skill.\n *\n * Resources are listed in L2 (skill content) so the agent knows\n * what is available, and loaded on demand in L3 via the `skill_resource` tool.\n *\n * For programmatically defined packs, provide `content` directly.\n * For local packs resolved from disk, `absolutePath` is set by the resolver.\n * At least one of `content` or `absolutePath` must be set.\n */\nexport interface PackSkillResource {\n /** Path relative to the skill directory. Used by the agent to request the resource. */\n relativePath: string;\n /** Resource classification. */\n type: PackSkillResourceType;\n /**\n * Inline resource content.\n *\n * Takes precedence over `absolutePath` when both are present.\n * Use this for programmatically defined packs.\n */\n content?: string;\n /**\n * Absolute filesystem path to the resource file.\n *\n * Set by the local pack resolver for on-disk resources.\n * The content is loaded lazily when the agent calls `skill_resource`.\n */\n absolutePath?: string;\n}\n\n/**\n * An inline skill definition inside a CapabilityPack.\n *\n * Carries the skill's name, description (L1), and full instruction body (L2)\n * so no file read is needed at runtime. When registered via `PackLoader`,\n * both the metadata and content are available immediately.\n *\n * ## Authoring\n *\n * The `description` field is the most important: the agent reads it to decide\n * whether to activate this skill. Write it as trigger phrases for task types,\n * not as prose for humans.\n *\n * @example\n * ```ts\n * {\n * name: \"api-integration\",\n * description: \"Connect to external REST APIs with auth, retries, and typed responses.\",\n * body: \"# API Integration\\n\\nAlways use the `apiClient` helper...\",\n * version: \"1.0.0\",\n * tags: [\"api\", \"http\", \"typescript\"],\n * }\n * ```\n */\nexport interface PackSkill {\n /** Unique skill name. Must not conflict with project or user skills (project/user win). */\n name: string;\n /**\n * When-to-use description. The agent reads this at L1 to decide whether to\n * activate the skill. Written for LLM consumption, not humans.\n */\n description: string;\n /**\n * Full SKILL.md markdown body (everything below the frontmatter block).\n * This becomes the L2 content loaded when the agent calls the `skill` tool.\n */\n body: string;\n /** SemVer version string. */\n version?: string;\n /** Categorization tags for filtering and discovery. */\n tags?: string[];\n /** Names of skills this skill works best alongside. Suggested at L2 load time. */\n dependencies?: string[];\n /**\n * Bundled reference material, scripts, templates, and examples (L3).\n *\n * The agent sees these listed after loading the skill (L2) and can\n * read individual resources via the `skill_resource` tool.\n */\n resources?: PackSkillResource[];\n}\n\n// ============================================================================\n// CapabilityPack\n// ============================================================================\n\n/**\n * A capability pack — the distributable unit of agent capabilities.\n *\n * A pack bundles any combination of:\n * - **Skills**: on-demand instructions loaded by the agent via the `skill` tool.\n * - **MCP servers**: external tool and resource providers connected via the MCP protocol.\n * - **Plugin**: tools, middleware, commands, and prompt sections contributed as code.\n *\n * Packs are designed to be:\n * - **Authored inline** in your agent code with `defineCapabilityPack()`\n * - **Published as npm packages** that default-export a `CapabilityPack`\n * - **Hosted at a URL** serving the JSON representation\n * - **Stored in a local directory** with a `pack.json` manifest\n *\n * The `PackLoader` feeds a pack's contributions into the agent-core subsystems\n * (`SkillRegistry`, `PluginRegistry`, and the `MCPConfig` for `MCPManager`).\n *\n * @example Inline pack with everything\n * ```ts\n * import { defineCapabilityPack } from \"@cuylabs/agent-capability-pack\"\n * import { definePlugin, stdioServer } from \"@cuylabs/agent-core\"\n *\n * export default defineCapabilityPack({\n * id: \"my-company-tools\",\n * name: \"My Company Tools\",\n * version: \"1.0.0\",\n *\n * skills: [{\n * name: \"api-integration\",\n * description: \"Connect to My Company internal APIs with auth and retry logic.\",\n * body: \"# API Integration\\n\\nAlways use the Bearer token from...\",\n * }],\n *\n * mcpServers: {\n * \"company-mcp\": stdioServer(\"npx\", [\"-y\", \"@mycompany/mcp-server\"]),\n * },\n *\n * plugin: definePlugin({\n * id: \"my-company-tools\",\n * tools: [myCustomTool],\n * }),\n * })\n * ```\n */\nexport interface CapabilityPack extends PackMeta {\n /**\n * Skills contributed to the agent's `SkillRegistry`.\n *\n * Skills use progressive disclosure: name and description (L1) are always\n * visible to the agent in the system prompt; full body (L2) is loaded when\n * the agent calls the `skill` tool.\n */\n skills?: PackSkill[];\n\n /**\n * MCP server configurations to add to the agent's `MCPManager`.\n *\n * These are collected by `PackLoader` and returned in `PackLoadResult.mcpServers`\n * for the host to merge into its MCPConfig before calling `mcpManager.connect()`.\n *\n * Use the `stdioServer()`, `httpServer()`, and `sseServer()` helpers from\n * `@cuylabs/agent-core/mcp` to build configs ergonomically.\n */\n mcpServers?: Record<string, MCPServerConfig>;\n\n /**\n * Plugin contributions: tools, middleware, commands, and prompt sections.\n *\n * Accepts either:\n * - A `PluginDefinition` object (from `definePlugin()` in agent-core)\n * - A raw `PluginInit` function\n *\n * The plugin is loaded into the host's `PluginRegistry` if one is provided\n * to `PackLoader`. The pack's `id` is used as the plugin ID if the plugin\n * definition does not specify one.\n */\n plugin?: PluginDefinition | PluginInit;\n}\n","/**\n * Pack manifest validation — shared by local and URL resolvers.\n */\n\nimport {\n CAPABILITY_PACK_KIND,\n CAPABILITY_PACK_SCHEMA_VERSION,\n type CapabilityPack,\n} from \"../pack/types.js\";\n\n/**\n * Validate that a parsed JSON object has the required `id` field and\n * nothing obviously wrong with its structure.\n *\n * This is intentionally lightweight — the host's MCPManager and\n * PluginRegistry will catch deeper structural errors at load time.\n *\n * @throws {Error} Validation failed.\n */\nexport function validatePackManifest(\n manifest: Record<string, unknown>,\n source: string,\n): asserts manifest is Record<string, unknown> & { id: string } {\n if (typeof manifest.id !== \"string\" || manifest.id.trim() === \"\") {\n throw new Error(\n `Pack manifest at \"${source}\" is missing a required string \"id\" field.`,\n );\n }\n if (manifest.id.includes(\" \")) {\n throw new Error(\n `Pack manifest at \"${source}\" has an invalid \"id\" — must not contain spaces (use kebab-case).`,\n );\n }\n if (manifest.kind !== undefined && manifest.kind !== CAPABILITY_PACK_KIND) {\n throw new Error(\n `Pack manifest at \"${source}\" has unsupported \"kind\" \"${String(manifest.kind)}\". ` +\n `Expected \"${CAPABILITY_PACK_KIND}\".`,\n );\n }\n if (\n manifest.schemaVersion !== undefined &&\n manifest.schemaVersion !== CAPABILITY_PACK_SCHEMA_VERSION\n ) {\n throw new Error(\n `Pack manifest at \"${source}\" has unsupported schemaVersion \"${String(manifest.schemaVersion)}\". ` +\n `Expected ${CAPABILITY_PACK_SCHEMA_VERSION}.`,\n );\n }\n}\n\n/**\n * Validate that a URL-sourced pack manifest does not include a `plugin` field.\n *\n * Arbitrary code cannot be loaded from a URL for security reasons.\n * URL packs are limited to metadata, skills, and MCP server configs.\n */\nexport function assertNoPluginInUrlPack(\n manifest: Partial<CapabilityPack>,\n url: string,\n): void {\n if (manifest.plugin !== undefined) {\n throw new Error(\n `Pack from URL \"${url}\" declares a plugin, which is not allowed. ` +\n `Plugin code can only be loaded from local files or npm packages.`,\n );\n }\n}\n\nexport function normalizePackManifest(pack: CapabilityPack): CapabilityPack {\n return {\n kind: pack.kind ?? CAPABILITY_PACK_KIND,\n schemaVersion: pack.schemaVersion ?? CAPABILITY_PACK_SCHEMA_VERSION,\n ...pack,\n };\n}\n","/**\n * Inline pack resolver — identity, no I/O.\n */\n\nimport type { CapabilityPack } from \"../pack/types.js\";\nimport type { InlinePackRef } from \"./types.js\";\nimport { normalizePackManifest } from \"./validate.js\";\n\n/** Return the pack as-is. No resolution needed. */\nexport function resolveInlinePack(ref: InlinePackRef): CapabilityPack {\n return normalizePackManifest(ref.pack);\n}\n","/**\n * Local pack resolver — reads a pack from a directory on disk.\n *\n * Directory structure expected:\n *\n * my-pack/\n * ├── pack.json # Required — pack metadata + mcpServers\n * └── skills/ # Optional — skill subdirectories (SKILL.md pattern)\n * ├── my-skill/\n * │ ├── SKILL.md\n * │ ├── references/\n * │ └── scripts/\n * └── another-skill/\n * └── SKILL.md\n *\n * Skills are discovered using agent-core's `discoverSkills()` and their\n * content is read eagerly (bodies are small). Resources keep their\n * `absolutePath` for lazy L3 loading.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { loadPluginModule } from \"@cuylabs/agent-core/plugin\";\nimport { discoverSkills, loadSkillContent } from \"@cuylabs/agent-core/skill\";\nimport type {\n CapabilityPack,\n PackSkill,\n PackSkillResource,\n} from \"../pack/types.js\";\nimport type { LocalPackRef } from \"./types.js\";\nimport { normalizePackManifest, validatePackManifest } from \"./validate.js\";\n\ntype LocalPackManifest = Record<string, unknown> & {\n id: string;\n name?: string;\n description?: string;\n version?: string;\n mcpServers?: CapabilityPack[\"mcpServers\"];\n plugin?: string;\n};\n\n/**\n * Resolve a local directory reference into a `CapabilityPack`.\n *\n * Reads `pack.json` for metadata and MCP configs, then scans the\n * `skills/` subdirectory for SKILL.md files and reads their bodies\n * eagerly so the pack is self-contained.\n *\n * @throws {Error} If `pack.json` is missing, invalid, or unreadable.\n */\nexport async function resolveLocalPack(\n ref: LocalPackRef,\n): Promise<CapabilityPack> {\n const absPath = resolve(ref.path);\n\n // Read and validate pack.json.\n const manifest = (await readPackJson(absPath)) as LocalPackManifest;\n validatePackManifest(manifest, absPath);\n\n // Discover and inline skills from the skills/ subdirectory.\n const skills = await discoverAndInlineSkills(absPath);\n const plugin = await resolveLocalPlugin(absPath, manifest.plugin);\n\n return normalizePackManifest({\n id: manifest.id,\n name: manifest.name,\n description: manifest.description,\n version: manifest.version,\n skills,\n mcpServers: manifest.mcpServers,\n plugin,\n });\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nasync function readPackJson(packDir: string): Promise<Record<string, unknown>> {\n const packJsonPath = join(packDir, \"pack.json\");\n let raw: string;\n try {\n raw = await readFile(packJsonPath, \"utf-8\");\n } catch {\n throw new Error(\n `Cannot read pack.json at ${packJsonPath}. ` +\n `Ensure the directory is a valid capability pack.`,\n );\n }\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`pack.json at ${packJsonPath} contains invalid JSON.`);\n }\n}\n\nasync function discoverAndInlineSkills(packDir: string): Promise<PackSkill[]> {\n const skillsDir = join(packDir, \"skills\");\n\n // Use agent-core discovery scoped to the pack's skills/ subdirectory.\n const discovery = await discoverSkills(packDir, {\n roots: [skillsDir],\n // One extra level to handle skills/my-skill/SKILL.md.\n maxScanDepth: 3,\n // Disable external directory scanning — we only want this pack's skills.\n externalDirs: [],\n });\n\n const packSkills: PackSkill[] = [];\n for (const metadata of discovery.skills) {\n // Read L2 content eagerly — bodies are small and we want the pack\n // to be self-contained so PackLoader can pre-cache without file reads.\n const content = await loadSkillContent(metadata);\n\n const resources: PackSkillResource[] = content.resources.map((r) => ({\n relativePath: r.relativePath,\n type: r.type as PackSkillResource[\"type\"],\n // Keep absolutePath — the resource file is on disk and will be\n // loaded lazily when the agent calls skill_resource.\n absolutePath: r.absolutePath,\n }));\n\n packSkills.push({\n name: metadata.name,\n description: metadata.description,\n body: content.body,\n version: metadata.version,\n tags: metadata.tags,\n dependencies: metadata.dependencies,\n resources: resources.length > 0 ? resources : undefined,\n });\n }\n\n return packSkills;\n}\n\nasync function resolveLocalPlugin(\n packDir: string,\n pluginEntry: string | undefined,\n): Promise<CapabilityPack[\"plugin\"] | undefined> {\n if (!pluginEntry) {\n return undefined;\n }\n\n if (typeof pluginEntry !== \"string\" || pluginEntry.trim() === \"\") {\n throw new Error(\n `Invalid \"plugin\" entry in pack.json at ${packDir}. ` +\n `Expected a non-empty relative module path.`,\n );\n }\n\n return loadPluginModule(resolve(packDir, pluginEntry));\n}\n","/**\n * npm pack resolver — imports a pack from an installed npm package.\n *\n * The package must export a `CapabilityPack` as its default export or\n * under a named export specified in `ref.export`. Supports both ESM\n * and CommonJS packages via dynamic `import()`.\n *\n * @example Pack package (the npm module you publish):\n * ```ts\n * // @mycompany/agent-pack/index.ts\n * import { defineCapabilityPack } from \"@cuylabs/agent-capability-pack\"\n * export default defineCapabilityPack({ id: \"my-pack\", ... })\n * ```\n *\n * @example Referencing it:\n * ```ts\n * npmPack(\"@mycompany/agent-pack\")\n * npmPack(\"@mycompany/agent-pack\", { export: \"dataPack\" })\n * ```\n */\n\nimport type { CapabilityPack } from \"../pack/types.js\";\nimport type { NpmPackRef } from \"./types.js\";\nimport { normalizePackManifest, validatePackManifest } from \"./validate.js\";\n\n/**\n * Resolve an npm package reference into a `CapabilityPack`.\n *\n * @throws {Error} Package not found, import failed, or manifest is invalid.\n */\nexport async function resolveNpmPack(ref: NpmPackRef): Promise<CapabilityPack> {\n let mod: Record<string, unknown>;\n try {\n mod = (await import(ref.package)) as Record<string, unknown>;\n } catch (err) {\n throw new Error(\n `Failed to import pack package \"${ref.package}\": ${err instanceof Error ? err.message : String(err)}. ` +\n `Ensure the package is installed in your workspace.`,\n );\n }\n\n // Resolve the pack from the named export or default export.\n const exportName = ref.export ?? \"default\";\n const value = mod[exportName];\n\n if (value === undefined) {\n const available = Object.keys(mod).join(\", \") || \"(none)\";\n throw new Error(\n `Package \"${ref.package}\" does not have a \"${exportName}\" export. ` +\n `Available exports: ${available}.`,\n );\n }\n\n // The export may be a plain object or wrapped by defineCapabilityPack().\n const pack = value as Record<string, unknown>;\n\n validatePackManifest(pack, `${ref.package}#${exportName}`);\n\n return normalizePackManifest(pack as unknown as CapabilityPack);\n}\n","/**\n * URL pack resolver — fetches a pack manifest from an HTTPS URL.\n *\n * URL packs are limited to metadata, skills, and MCP server configs.\n * Plugin code is explicitly blocked — arbitrary code cannot be loaded\n * from a URL for security reasons.\n *\n * The URL must return a JSON object matching `CapabilityPack`.\n */\n\nimport type { CapabilityPack } from \"../pack/types.js\";\nimport type { UrlPackRef } from \"./types.js\";\nimport {\n assertNoPluginInUrlPack,\n normalizePackManifest,\n validatePackManifest,\n} from \"./validate.js\";\n\n/** Fetch timeout for pack manifests (15 seconds). */\nconst FETCH_TIMEOUT_MS = 15_000;\n\n/**\n * Resolve a URL reference into a `CapabilityPack`.\n *\n * @throws {Error} Fetch failed, timed out, or manifest is invalid.\n */\nexport async function resolveUrlPack(ref: UrlPackRef): Promise<CapabilityPack> {\n // Only HTTPS is allowed — plain HTTP would allow MITM injection.\n if (!ref.url.startsWith(\"https://\")) {\n throw new Error(\n `URL pack source must use HTTPS. Got: \"${ref.url}\". ` +\n `Plain HTTP is not allowed — capability packs must be served over a secure connection.`,\n );\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(ref.url, {\n signal: controller.signal,\n headers: { Accept: \"application/json\" },\n });\n } catch (err) {\n throw new Error(\n `Failed to fetch pack from \"${ref.url}\": ${err instanceof Error ? err.message : String(err)}.`,\n );\n } finally {\n clearTimeout(timeout);\n }\n\n if (!response.ok) {\n throw new Error(\n `Pack fetch from \"${ref.url}\" returned HTTP ${response.status} ${response.statusText}.`,\n );\n }\n\n let manifest: Record<string, unknown>;\n try {\n manifest = (await response.json()) as Record<string, unknown>;\n } catch {\n throw new Error(`Pack from \"${ref.url}\" did not return valid JSON.`);\n }\n\n validatePackManifest(manifest, ref.url);\n\n const pack = manifest as unknown as Partial<CapabilityPack>;\n\n // Security: URL packs must not contain plugin code.\n assertNoPluginInUrlPack(pack, ref.url);\n\n return normalizePackManifest(pack as CapabilityPack);\n}\n","/**\n * Git pack resolver — clones or fetches a pack from a Git repository.\n *\n * Requires `git` to be available on PATH. The repository root must\n * contain a `pack.json` manifest. Skills are discovered the same way\n * as `LocalPackSource` after the repository is checked out.\n *\n * Repository data is cached under `~/.cache/cuylabs/packs/git/` keyed\n * by a hash of the URL + ref so repeated resolutions skip the clone.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir, rm, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { CapabilityPack } from \"../pack/types.js\";\nimport type { GitPackRef } from \"./types.js\";\nimport { resolveLocalPack } from \"./local-source.js\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Default git cache directory. */\nfunction gitCacheRoot(): string {\n return join(homedir(), \".cache\", \"cuylabs\", \"packs\", \"git\");\n}\n\n/**\n * Derive a filesystem-safe directory name from a git URL and optional ref.\n *\n * Prefixes a readable repository slug with a short content hash to avoid\n * collisions between similarly named remotes and refs.\n */\nfunction slugify(url: string, ref?: string): string {\n const readable = url\n .replace(/^https?:\\/\\//, \"\")\n .replace(/^git@/, \"\")\n .replace(/[/:. ]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/\\.git$/, \"\");\n const hash = createHash(\"sha256\")\n .update(`${url}#${ref ?? \"\"}`)\n .digest(\"hex\")\n .slice(0, 12);\n return `${readable}--${hash}`;\n}\n\n/**\n * Resolve a Git repository reference into a `CapabilityPack`.\n *\n * Clones the repository (sparse, single-depth) into a local cache\n * directory. On subsequent calls, pulls the latest changes if the\n * target ref is a branch name. Fixed refs (commit SHAs) are\n * not re-fetched once cloned.\n *\n * @throws {Error} git is unavailable, clone failed, or pack.json is missing.\n */\nexport async function resolveGitPack(ref: GitPackRef): Promise<CapabilityPack> {\n const cacheRoot = gitCacheRoot();\n const repoDir = join(cacheRoot, slugify(ref.url, ref.ref));\n\n await ensureCloned(repoDir, ref);\n\n return resolveLocalPack({ source: \"local\", path: repoDir });\n}\n\n// ============================================================================\n// Git helpers\n// ============================================================================\n\nasync function ensureCloned(repoDir: string, ref: GitPackRef): Promise<void> {\n const alreadyCloned = await directoryExists(repoDir);\n\n if (!alreadyCloned) {\n await mkdir(gitCacheRoot(), { recursive: true });\n await gitClone(ref.url, ref.ref, repoDir);\n } else if (!isFixedRef(ref.ref)) {\n // Branch refs may have new commits — pull to stay current.\n await gitPull(repoDir);\n }\n // Fixed refs (commit SHAs) are not re-fetched.\n}\n\n/**\n * A \"fixed\" ref is a commit SHA.\n *\n * This heuristic avoids re-fetching immutable references.\n * Branches are NOT fixed — they can receive new commits.\n */\nfunction isFixedRef(ref?: string): boolean {\n if (!ref) return false;\n // Full commit SHA\n if (/^[0-9a-f]{40}$/i.test(ref)) return true;\n // Short SHA (>= 7 chars)\n if (/^[0-9a-f]{7,}$/i.test(ref)) return true;\n return false;\n}\n\nasync function gitClone(\n url: string,\n ref: string | undefined,\n target: string,\n): Promise<void> {\n const args = [\"clone\", \"--depth=1\"];\n if (ref && !isFixedRef(ref)) args.push(\"--branch\", ref);\n args.push(url, target);\n try {\n await git(args, \".\");\n if (ref && isFixedRef(ref)) {\n await git([\"fetch\", \"--depth=1\", \"origin\", ref], target);\n await git([\"checkout\", \"FETCH_HEAD\"], target);\n }\n } catch (err) {\n await rm(target, { recursive: true, force: true });\n throw err;\n }\n}\n\nasync function gitPull(repoDir: string): Promise<void> {\n await git([\"pull\", \"--ff-only\"], repoDir);\n}\n\nasync function git(args: string[], cwd: string): Promise<void> {\n try {\n await execFileAsync(\"git\", args, { cwd });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`git ${args[0]} failed: ${msg}`);\n }\n}\n\nasync function directoryExists(path: string): Promise<boolean> {\n try {\n const info = await stat(path);\n return info.isDirectory();\n } catch {\n return false;\n }\n}\n","/**\n * resolvePackRef — unified entry point for resolving any PackRef.\n */\n\nimport type { CapabilityPack } from \"../pack/types.js\";\nimport type { PackRef } from \"./types.js\";\nimport { resolveInlinePack } from \"./inline-source.js\";\nimport { resolveLocalPack } from \"./local-source.js\";\nimport { resolveNpmPack } from \"./npm-source.js\";\nimport { resolveUrlPack } from \"./url-source.js\";\nimport { resolveGitPack } from \"./git-source.js\";\n\n/**\n * Resolve a `PackRef` to a `CapabilityPack`.\n *\n * Dispatches to the appropriate resolver based on `ref.source`.\n * Throws if resolution fails (network error, missing file, bad manifest, etc.).\n *\n * @throws {Error} Resolution failed — details in the error message.\n */\nexport async function resolvePackRef(ref: PackRef): Promise<CapabilityPack> {\n switch (ref.source) {\n case \"inline\":\n return resolveInlinePack(ref);\n case \"local\":\n return resolveLocalPack(ref);\n case \"npm\":\n return resolveNpmPack(ref);\n case \"url\":\n return resolveUrlPack(ref);\n case \"git\":\n return resolveGitPack(ref);\n default: {\n // Exhaustive check — TypeScript will catch unhandled variants.\n const _exhaustive: never = ref;\n throw new Error(\n `Unknown pack source: \"${(_exhaustive as { source: string }).source}\"`,\n );\n }\n }\n}\n"],"mappings":"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","names":["join","join"]}
@@ -0,0 +1,220 @@
1
+ import {
2
+ CAPABILITY_PACK_KIND,
3
+ CAPABILITY_PACK_SCHEMA_VERSION,
4
+ resolvePackRef
5
+ } from "./chunk-5E66GFAI.js";
6
+
7
+ // src/pack/define.ts
8
+ function defineCapabilityPack(pack) {
9
+ return {
10
+ kind: pack.kind ?? CAPABILITY_PACK_KIND,
11
+ schemaVersion: pack.schemaVersion ?? CAPABILITY_PACK_SCHEMA_VERSION,
12
+ ...pack
13
+ };
14
+ }
15
+
16
+ // src/pack/loader.ts
17
+ import { definePlugin } from "@cuylabs/agent-core/plugin";
18
+ var PackLoader = class {
19
+ skillRegistry;
20
+ pluginRegistry;
21
+ cwd;
22
+ constructor(options) {
23
+ this.skillRegistry = options.skillRegistry;
24
+ this.pluginRegistry = options.pluginRegistry;
25
+ this.cwd = options.cwd ?? process.cwd();
26
+ }
27
+ // ==========================================================================
28
+ // Public API
29
+ // ==========================================================================
30
+ /**
31
+ * Load multiple packs in order.
32
+ *
33
+ * Accepts a mix of resolved `CapabilityPack` objects and `PackRef`
34
+ * references. Each item is processed sequentially; one failure does not
35
+ * block the rest.
36
+ *
37
+ * @returns Aggregate result with per-pack outcomes and collected MCP configs.
38
+ */
39
+ async loadAll(items) {
40
+ const loaded = [];
41
+ const failed = [];
42
+ const aggregatedMcpServers = {};
43
+ for (const item of items) {
44
+ let pack;
45
+ if (isPackRef(item)) {
46
+ try {
47
+ pack = await resolvePackRef(item);
48
+ } catch (err) {
49
+ failed.push({
50
+ packId: describePackInput(item),
51
+ skillsRegistered: 0,
52
+ mcpServerNames: [],
53
+ pluginLoaded: false,
54
+ error: errorMessage(err)
55
+ });
56
+ continue;
57
+ }
58
+ } else {
59
+ pack = item;
60
+ }
61
+ const result = await this.loadOne(pack);
62
+ if (result.error) {
63
+ failed.push(result);
64
+ } else {
65
+ loaded.push(result);
66
+ if (pack.mcpServers) {
67
+ Object.assign(aggregatedMcpServers, pack.mcpServers);
68
+ }
69
+ }
70
+ }
71
+ return { loaded, failed, mcpServers: aggregatedMcpServers };
72
+ }
73
+ /**
74
+ * Load a single resolved `CapabilityPack`.
75
+ *
76
+ * Stages pack content before mutating the runtime registries. Plugins are
77
+ * loaded first; only successful packs contribute skills and MCP configs.
78
+ */
79
+ async loadOne(pack) {
80
+ try {
81
+ const stagedSkills = (pack.skills ?? []).map(
82
+ (skill) => this.preparePackSkill(pack.id, skill)
83
+ );
84
+ const mcpServerNames = Object.keys(pack.mcpServers ?? {});
85
+ const pluginResult = await this.loadPluginContribution(pack);
86
+ if (pluginResult.error) {
87
+ return {
88
+ packId: pack.id,
89
+ skillsRegistered: 0,
90
+ mcpServerNames: [],
91
+ pluginLoaded: false,
92
+ error: pluginResult.error
93
+ };
94
+ }
95
+ let skillsRegistered = 0;
96
+ for (const staged of stagedSkills) {
97
+ const existing = this.skillRegistry.get(staged.metadata.name);
98
+ this.skillRegistry.register(staged.metadata, staged.content);
99
+ if (canRegisterExternalSkill(existing)) {
100
+ skillsRegistered++;
101
+ }
102
+ }
103
+ return {
104
+ packId: pack.id,
105
+ skillsRegistered,
106
+ mcpServerNames,
107
+ pluginLoaded: pluginResult.loaded
108
+ };
109
+ } catch (err) {
110
+ return {
111
+ packId: pack.id,
112
+ skillsRegistered: 0,
113
+ mcpServerNames: [],
114
+ pluginLoaded: false,
115
+ error: errorMessage(err)
116
+ };
117
+ }
118
+ }
119
+ // ==========================================================================
120
+ // Internal helpers
121
+ // ==========================================================================
122
+ /**
123
+ * Convert a `PackSkill` into `SkillMetadata` + `SkillContent` and register
124
+ * both in the skill registry so the agent can discover (L1) and load (L2)
125
+ * the skill without any file reads.
126
+ *
127
+ * Discovered skills (project / user scope) always win if a skill with the
128
+ * same name already exists — see `SkillRegistry.register()`.
129
+ */
130
+ preparePackSkill(packId, skill) {
131
+ const source = { type: "remote", root: `pack://${packId}` };
132
+ const metadata = {
133
+ name: skill.name,
134
+ description: skill.description,
135
+ version: skill.version,
136
+ // Pack-contributed skills have "global" scope — lower priority than
137
+ // project/user skills with the same name.
138
+ scope: "global",
139
+ source,
140
+ // No file on disk — filePath and baseDir are intentionally absent.
141
+ tags: skill.tags,
142
+ dependencies: skill.dependencies
143
+ };
144
+ const content = {
145
+ ...metadata,
146
+ body: skill.body,
147
+ resources: (skill.resources ?? []).map((r) => ({
148
+ relativePath: r.relativePath,
149
+ type: r.type,
150
+ content: r.content,
151
+ absolutePath: r.absolutePath
152
+ }))
153
+ };
154
+ return { metadata, content };
155
+ }
156
+ async loadPluginContribution(pack) {
157
+ if (pack.plugin === void 0 || this.pluginRegistry === void 0) {
158
+ return { loaded: false };
159
+ }
160
+ const beforeCount = this.pluginRegistry.pluginCount;
161
+ const beforeErrors = this.pluginRegistry.contributions.errors.length;
162
+ const init = typeof pack.plugin === "function" ? pack.plugin : definePlugin(pack.plugin);
163
+ const manifest = buildPluginManifest(pack);
164
+ await this.pluginRegistry.loadOne(init, manifest, this.cwd);
165
+ const loaded = this.pluginRegistry.pluginCount > beforeCount;
166
+ if (loaded) {
167
+ return { loaded: true };
168
+ }
169
+ const newErrors = this.pluginRegistry.contributions.errors.slice(beforeErrors);
170
+ return {
171
+ loaded: false,
172
+ error: newErrors.at(-1)?.error ?? `Plugin contribution from pack "${pack.id}" was rejected by the plugin registry.`
173
+ };
174
+ }
175
+ };
176
+ function isPackRef(item) {
177
+ return "source" in item && typeof item.source === "string";
178
+ }
179
+ function buildPluginManifest(pack) {
180
+ return {
181
+ id: pack.id,
182
+ name: pack.name,
183
+ description: pack.description,
184
+ version: pack.version
185
+ };
186
+ }
187
+ function errorMessage(err) {
188
+ return err instanceof Error ? err.message : String(err);
189
+ }
190
+ function canRegisterExternalSkill(existing) {
191
+ if (!existing) {
192
+ return true;
193
+ }
194
+ return !(existing.scope === "builtin" || existing.source.type === "local" || existing.filePath !== void 0 || existing.baseDir !== void 0);
195
+ }
196
+ function describePackInput(item) {
197
+ if (!isPackRef(item)) {
198
+ return item.id;
199
+ }
200
+ switch (item.source) {
201
+ case "inline":
202
+ return item.pack.id;
203
+ case "local":
204
+ return `local:${item.path}`;
205
+ case "npm":
206
+ return `npm:${item.package}`;
207
+ case "url":
208
+ return `url:${item.url}`;
209
+ case "git":
210
+ return `git:${item.url}${item.ref ? `#${item.ref}` : ""}`;
211
+ default:
212
+ return "(unresolved)";
213
+ }
214
+ }
215
+
216
+ export {
217
+ defineCapabilityPack,
218
+ PackLoader
219
+ };
220
+ //# sourceMappingURL=chunk-IUGGMU63.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pack/define.ts","../src/pack/loader.ts"],"sourcesContent":["/**\n * defineCapabilityPack — ergonomic authoring helper.\n *\n * Returns the pack with default manifest metadata filled in. The value lies in\n * IDE completion, type narrowing, and the ability to statically inspect packs\n * without executing them.\n */\n\nimport type { CapabilityPack } from \"./types.js\";\nimport {\n CAPABILITY_PACK_KIND,\n CAPABILITY_PACK_SCHEMA_VERSION,\n} from \"./types.js\";\n\n/**\n * Define a capability pack with full type checking.\n *\n * This is the canonical way to author a pack inline or in an npm package.\n * The function preserves your pack shape and fills default manifest metadata\n * when `kind` or `schemaVersion` are omitted.\n *\n * @example\n * ```ts\n * // my-pack.ts\n * import { defineCapabilityPack } from \"@cuylabs/agent-capability-pack\"\n *\n * export default defineCapabilityPack({\n * id: \"my-pack\",\n * name: \"My Pack\",\n * version: \"1.0.0\",\n * skills: [{\n * name: \"my-skill\",\n * description: \"Handles X when the task involves Y.\",\n * body: \"# My Skill\\n\\nInstructions...\",\n * }],\n * })\n * ```\n */\nexport function defineCapabilityPack(pack: CapabilityPack): CapabilityPack {\n return {\n kind: pack.kind ?? CAPABILITY_PACK_KIND,\n schemaVersion: pack.schemaVersion ?? CAPABILITY_PACK_SCHEMA_VERSION,\n ...pack,\n };\n}\n","/**\n * PackLoader — bridges CapabilityPacks into agent-core subsystems.\n *\n * The loader is the orchestration layer between the pack distribution\n * format and the three runtime systems it feeds:\n *\n * pack.skills → SkillRegistry.register()\n * pack.mcpServers → returned in PackLoadResult for host to merge\n * pack.plugin → PluginRegistry.loadOne()\n *\n * MCP server configs are not applied directly to an MCPManager because\n * the manager must receive all its server configs before `connect()` is\n * called. The host merges `result.mcpServers` into its own config and\n * constructs the manager — this preserves the manager's contract.\n *\n * @example\n * ```ts\n * import { PackLoader } from \"@cuylabs/agent-capability-pack\"\n * import { createSkillRegistry } from \"@cuylabs/agent-core\"\n * import { createMCPManager } from \"@cuylabs/agent-core/mcp\"\n * import { PluginRegistry } from \"@cuylabs/agent-core/plugin\"\n *\n * const skillRegistry = await createSkillRegistry(cwd)\n * const pluginRegistry = new PluginRegistry()\n *\n * const loader = new PackLoader({ skillRegistry, pluginRegistry, cwd })\n * const result = await loader.loadAll([\n * { source: \"inline\", pack: myPack },\n * { source: \"npm\", package: \"@myorg/agent-pack\" },\n * { source: \"local\", path: \"./packs/company-tools\" },\n * ])\n *\n * // Merge pack-contributed servers with your own before connecting\n * const mcpManager = createMCPManager({ ...myServers, ...result.mcpServers })\n * await mcpManager.connect()\n * ```\n */\n\nimport type {\n SkillContent,\n SkillMetadata,\n SkillRegistry,\n} from \"@cuylabs/agent-core/skill\";\nimport type {\n PluginManifest,\n PluginRegistry,\n} from \"@cuylabs/agent-core/plugin\";\nimport type { MCPConfig } from \"@cuylabs/agent-core/mcp\";\nimport { definePlugin } from \"@cuylabs/agent-core/plugin\";\nimport type { CapabilityPack, PackSkill } from \"./types.js\";\nimport type { PackItemResult, PackLoadResult } from \"./result.js\";\nimport type { PackRef } from \"../sources/types.js\";\nimport { resolvePackRef } from \"../sources/resolve.js\";\n\n// ============================================================================\n// Options\n// ============================================================================\n\nexport interface PackLoaderOptions {\n /**\n * The skill registry that pack skills are registered into.\n *\n * Skills contributed by a pack have \"global\" scope and lose to any\n * project- or user-scoped skill with the same name (scoped skills win).\n */\n skillRegistry: SkillRegistry;\n /**\n * Optional plugin registry for loading pack plugin contributions.\n *\n * When omitted, plugin contributions from packs are silently ignored.\n * Provide a `PluginRegistry` instance if your agent uses the plugin system.\n */\n pluginRegistry?: PluginRegistry;\n /**\n * Working directory passed to the plugin registry when loading pack plugins.\n *\n * Defaults to `process.cwd()`.\n */\n cwd?: string;\n}\n\n// ============================================================================\n// PackLoader\n// ============================================================================\n\n/**\n * Loads capability packs into agent-core subsystems.\n *\n * Accepts both resolved `CapabilityPack` objects and unresolved `PackRef`\n * references. Refs are resolved before loading; resolution errors are\n * reported in `PackLoadResult.failed` rather than throwing.\n *\n * The loader is non-throwing by default: individual pack failures are\n * collected and returned so the agent can start with whatever capabilities\n * loaded successfully.\n */\nexport class PackLoader {\n private readonly skillRegistry: SkillRegistry;\n private readonly pluginRegistry: PluginRegistry | undefined;\n private readonly cwd: string;\n\n constructor(options: PackLoaderOptions) {\n this.skillRegistry = options.skillRegistry;\n this.pluginRegistry = options.pluginRegistry;\n this.cwd = options.cwd ?? process.cwd();\n }\n\n // ==========================================================================\n // Public API\n // ==========================================================================\n\n /**\n * Load multiple packs in order.\n *\n * Accepts a mix of resolved `CapabilityPack` objects and `PackRef`\n * references. Each item is processed sequentially; one failure does not\n * block the rest.\n *\n * @returns Aggregate result with per-pack outcomes and collected MCP configs.\n */\n async loadAll(\n items: ReadonlyArray<CapabilityPack | PackRef>,\n ): Promise<PackLoadResult> {\n const loaded: PackItemResult[] = [];\n const failed: PackItemResult[] = [];\n const aggregatedMcpServers: MCPConfig = {};\n\n for (const item of items) {\n // Resolve PackRef → CapabilityPack if needed.\n let pack: CapabilityPack;\n if (isPackRef(item)) {\n try {\n pack = await resolvePackRef(item);\n } catch (err) {\n failed.push({\n packId: describePackInput(item),\n skillsRegistered: 0,\n mcpServerNames: [],\n pluginLoaded: false,\n error: errorMessage(err),\n });\n continue;\n }\n } else {\n pack = item;\n }\n\n const result = await this.loadOne(pack);\n if (result.error) {\n failed.push(result);\n } else {\n loaded.push(result);\n if (pack.mcpServers) {\n Object.assign(aggregatedMcpServers, pack.mcpServers);\n }\n }\n }\n\n return { loaded, failed, mcpServers: aggregatedMcpServers };\n }\n\n /**\n * Load a single resolved `CapabilityPack`.\n *\n * Stages pack content before mutating the runtime registries. Plugins are\n * loaded first; only successful packs contribute skills and MCP configs.\n */\n async loadOne(pack: CapabilityPack): Promise<PackItemResult> {\n try {\n const stagedSkills = (pack.skills ?? []).map((skill) =>\n this.preparePackSkill(pack.id, skill),\n );\n const mcpServerNames = Object.keys(pack.mcpServers ?? {});\n\n const pluginResult = await this.loadPluginContribution(pack);\n if (pluginResult.error) {\n return {\n packId: pack.id,\n skillsRegistered: 0,\n mcpServerNames: [],\n pluginLoaded: false,\n error: pluginResult.error,\n };\n }\n\n let skillsRegistered = 0;\n for (const staged of stagedSkills) {\n const existing = this.skillRegistry.get(staged.metadata.name);\n this.skillRegistry.register(staged.metadata, staged.content);\n if (canRegisterExternalSkill(existing)) {\n skillsRegistered++;\n }\n }\n\n return {\n packId: pack.id,\n skillsRegistered,\n mcpServerNames,\n pluginLoaded: pluginResult.loaded,\n };\n } catch (err) {\n return {\n packId: pack.id,\n skillsRegistered: 0,\n mcpServerNames: [],\n pluginLoaded: false,\n error: errorMessage(err),\n };\n }\n }\n\n // ==========================================================================\n // Internal helpers\n // ==========================================================================\n\n /**\n * Convert a `PackSkill` into `SkillMetadata` + `SkillContent` and register\n * both in the skill registry so the agent can discover (L1) and load (L2)\n * the skill without any file reads.\n *\n * Discovered skills (project / user scope) always win if a skill with the\n * same name already exists — see `SkillRegistry.register()`.\n */\n private preparePackSkill(\n packId: string,\n skill: PackSkill,\n ): {\n metadata: SkillMetadata;\n content: SkillContent;\n } {\n const source = { type: \"remote\" as const, root: `pack://${packId}` };\n\n const metadata: SkillMetadata = {\n name: skill.name,\n description: skill.description,\n version: skill.version,\n // Pack-contributed skills have \"global\" scope — lower priority than\n // project/user skills with the same name.\n scope: \"global\",\n source,\n // No file on disk — filePath and baseDir are intentionally absent.\n tags: skill.tags,\n dependencies: skill.dependencies,\n };\n\n const content: SkillContent = {\n ...metadata,\n body: skill.body,\n resources: (skill.resources ?? []).map((r) => ({\n relativePath: r.relativePath,\n type: r.type,\n content: r.content,\n absolutePath: r.absolutePath,\n })),\n };\n\n return { metadata, content };\n }\n\n private async loadPluginContribution(\n pack: CapabilityPack,\n ): Promise<{ loaded: boolean; error?: string }> {\n if (pack.plugin === undefined || this.pluginRegistry === undefined) {\n return { loaded: false };\n }\n\n const beforeCount = this.pluginRegistry.pluginCount;\n const beforeErrors = this.pluginRegistry.contributions.errors.length;\n const init =\n typeof pack.plugin === \"function\"\n ? pack.plugin\n : definePlugin(pack.plugin);\n const manifest = buildPluginManifest(pack);\n\n await this.pluginRegistry.loadOne(init, manifest, this.cwd);\n\n const loaded = this.pluginRegistry.pluginCount > beforeCount;\n if (loaded) {\n return { loaded: true };\n }\n\n const newErrors =\n this.pluginRegistry.contributions.errors.slice(beforeErrors);\n return {\n loaded: false,\n error:\n newErrors.at(-1)?.error ??\n `Plugin contribution from pack \"${pack.id}\" was rejected by the plugin registry.`,\n };\n }\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Discriminate between a CapabilityPack (has `id`) and a PackRef (has `source`).\n *\n * Both are plain objects, so we use the presence of the `source` field as the\n * discriminant — all PackRef variants require it, CapabilityPack does not.\n */\nfunction isPackRef(item: CapabilityPack | PackRef): item is PackRef {\n return \"source\" in item && typeof (item as PackRef).source === \"string\";\n}\n\n/**\n * Derive a minimal `PluginManifest` from pack metadata.\n *\n * The plugin's own manifest (if it was created with `definePlugin()`) takes\n * precedence once loaded — this is just the bootstrap entry for the registry.\n */\nfunction buildPluginManifest(pack: CapabilityPack): PluginManifest {\n return {\n id: pack.id,\n name: pack.name,\n description: pack.description,\n version: pack.version,\n };\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction canRegisterExternalSkill(\n existing: SkillMetadata | undefined,\n): boolean {\n if (!existing) {\n return true;\n }\n\n return !(\n existing.scope === \"builtin\" ||\n existing.source.type === \"local\" ||\n existing.filePath !== undefined ||\n existing.baseDir !== undefined\n );\n}\n\nfunction describePackInput(item: CapabilityPack | PackRef): string {\n if (!isPackRef(item)) {\n return item.id;\n }\n\n switch (item.source) {\n case \"inline\":\n return item.pack.id;\n case \"local\":\n return `local:${item.path}`;\n case \"npm\":\n return `npm:${item.package}`;\n case \"url\":\n return `url:${item.url}`;\n case \"git\":\n return `git:${item.url}${item.ref ? `#${item.ref}` : \"\"}`;\n default:\n return \"(unresolved)\";\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,168 @@
1
+ // src/bridge/mcp-skill-bridge.ts
2
+ var MCPSkillBridge = class {
3
+ mcpManager;
4
+ skillRegistry;
5
+ constructor(mcpManager, skillRegistry) {
6
+ this.mcpManager = mcpManager;
7
+ this.skillRegistry = skillRegistry;
8
+ }
9
+ /**
10
+ * Discover all prompts from connected MCP servers and register them as skills.
11
+ *
12
+ * @returns Summary of what was bridged and any errors encountered.
13
+ */
14
+ async bridge() {
15
+ const errors = [];
16
+ let bridgedCount = 0;
17
+ let prompts;
18
+ try {
19
+ prompts = await this.mcpManager.listPrompts();
20
+ } catch (err) {
21
+ errors.push({
22
+ server: "(all)",
23
+ error: err instanceof Error ? err.message : String(err)
24
+ });
25
+ return { bridgedCount, errors };
26
+ }
27
+ for (const prompt of prompts) {
28
+ try {
29
+ const { metadata, content, warning } = await this.buildPromptSkill(prompt);
30
+ const existing = this.skillRegistry.get(metadata.name);
31
+ this.skillRegistry.register(metadata, content);
32
+ if (canRegisterExternalSkill(existing)) {
33
+ bridgedCount++;
34
+ }
35
+ if (warning) {
36
+ errors.push({
37
+ server: prompt.server,
38
+ error: warning
39
+ });
40
+ }
41
+ } catch (err) {
42
+ errors.push({
43
+ server: prompt.server,
44
+ error: err instanceof Error ? err.message : String(err)
45
+ });
46
+ }
47
+ }
48
+ return { bridgedCount, errors };
49
+ }
50
+ // ==========================================================================
51
+ // Internal
52
+ // ==========================================================================
53
+ async buildPromptSkill(prompt) {
54
+ const metadata = {
55
+ name: `${prompt.server}:${prompt.name}`,
56
+ description: prompt.description ?? `MCP prompt "${prompt.name}" from server "${prompt.server}".`,
57
+ // MCP-bridged skills use "global" scope — local/project/user skills win.
58
+ scope: "global",
59
+ source: {
60
+ type: "remote",
61
+ root: `mcp://${prompt.server}`
62
+ }
63
+ };
64
+ if ((prompt.arguments?.length ?? 0) > 0) {
65
+ return {
66
+ metadata,
67
+ content: {
68
+ ...metadata,
69
+ body: formatPromptTemplate(prompt),
70
+ resources: []
71
+ }
72
+ };
73
+ }
74
+ try {
75
+ const result = await this.mcpManager.getPrompt(
76
+ prompt.server,
77
+ prompt.name
78
+ );
79
+ return {
80
+ metadata,
81
+ content: {
82
+ ...metadata,
83
+ body: formatPromptMessages(prompt, result.messages),
84
+ resources: []
85
+ }
86
+ };
87
+ } catch (err) {
88
+ return {
89
+ metadata,
90
+ content: {
91
+ ...metadata,
92
+ body: formatPromptTemplate(prompt),
93
+ resources: []
94
+ },
95
+ warning: `Failed to render MCP prompt "${prompt.name}" from "${prompt.server}": ${err instanceof Error ? err.message : String(err)}. Using prompt metadata fallback instead.`
96
+ };
97
+ }
98
+ }
99
+ };
100
+ function formatPromptMessages(prompt, messages) {
101
+ const lines = [`# MCP Prompt: ${prompt.server}/${prompt.name}`];
102
+ if (prompt.description) {
103
+ lines.push("");
104
+ lines.push(prompt.description);
105
+ }
106
+ if (messages.length === 0) return lines.join("\n");
107
+ if (messages.length === 1 && messages[0]) {
108
+ lines.push("");
109
+ lines.push(extractMessageText(messages[0]));
110
+ return lines.join("\n");
111
+ }
112
+ lines.push("");
113
+ lines.push(
114
+ messages.map((msg) => {
115
+ const role = msg.role === "user" ? "**User**" : msg.role === "assistant" ? "**Assistant**" : `**${msg.role}**`;
116
+ const text = extractMessageText(msg);
117
+ return `${role}:
118
+
119
+ ${text}`;
120
+ }).join("\n\n---\n\n")
121
+ );
122
+ return lines.join("\n");
123
+ }
124
+ function formatPromptTemplate(prompt) {
125
+ const lines = [`# MCP Prompt Template: ${prompt.server}/${prompt.name}`];
126
+ if (prompt.description) {
127
+ lines.push("");
128
+ lines.push(prompt.description);
129
+ }
130
+ const args = prompt.arguments ?? [];
131
+ if (args.length > 0) {
132
+ lines.push("");
133
+ lines.push("## Prompt Arguments");
134
+ for (const arg of args) {
135
+ const required = arg.required ? "required" : "optional";
136
+ lines.push(
137
+ `- \`${arg.name}\` (${required})${arg.description ? `: ${arg.description}` : ""}`
138
+ );
139
+ }
140
+ lines.push("");
141
+ lines.push(
142
+ "This prompt is parameterized. A concrete prompt body depends on runtime argument values."
143
+ );
144
+ }
145
+ return lines.join("\n");
146
+ }
147
+ function extractMessageText(message) {
148
+ if (Array.isArray(message.content)) {
149
+ return message.content.map(
150
+ (part) => part.type === "text" && part.text ? part.text : `[${part.type} content]`
151
+ ).join("\n\n");
152
+ }
153
+ if (message.content.type === "text" && message.content.text) {
154
+ return message.content.text;
155
+ }
156
+ return `[${message.content.type} content]`;
157
+ }
158
+ function canRegisterExternalSkill(existing) {
159
+ if (!existing) {
160
+ return true;
161
+ }
162
+ return !(existing.scope === "builtin" || existing.source.type === "local" || existing.filePath !== void 0 || existing.baseDir !== void 0);
163
+ }
164
+
165
+ export {
166
+ MCPSkillBridge
167
+ };
168
+ //# sourceMappingURL=chunk-LDYHPSNE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge/mcp-skill-bridge.ts"],"sourcesContent":["/**\n * MCPSkillBridge — surfaces MCP server prompts as skills in the SkillRegistry.\n *\n * This is the fix for the missing connection between the MCP and skill\n * subsystems. When an MCP server declares prompts (via `prompts/list`),\n * those prompts are activated the same way as file-based skills — the agent\n * calls the `skill` tool, the bridge fetches the prompt from the server and\n * returns its content as L2 skill content.\n *\n * ## How it works\n *\n * 1. `bridge()` calls `mcpManager.listPrompts()` to get all prompts from\n * connected servers.\n * 2. Each prompt becomes a `SkillMetadata` entry registered in the\n * `SkillRegistry` (L1 — always visible to the agent).\n * 3. Static prompts (no arguments) are rendered immediately via\n * `mcpManager.getPrompt()` and cached as L2 skill content.\n * 4. Parameterized prompts are converted into metadata-backed skill content\n * that lists prompt arguments and usage.\n *\n * ## Naming\n *\n * Bridged skill names are scoped to their server to avoid collisions:\n * `<serverName>:<promptName>`\n *\n * If a project or user skill with the same name exists, it wins\n * (the registry's `register()` respects scope priority).\n *\n * ## Usage\n *\n * Call `bridge()` AFTER `mcpManager.connect()` and before the agent loop:\n *\n * ```ts\n * await mcpManager.connect()\n * const bridge = new MCPSkillBridge(mcpManager, skillRegistry)\n * const result = await bridge.bridge()\n * // result.bridgedCount — skills registered from MCP prompts\n * // result.errors — servers that failed to list prompts\n * ```\n */\n\nimport type { MCPManager, MCPPromptInfo } from \"@cuylabs/agent-core/mcp\";\nimport type {\n SkillContent,\n SkillMetadata,\n SkillRegistry,\n} from \"@cuylabs/agent-core/skill\";\n\n// ============================================================================\n// Result type\n// ============================================================================\n\nexport interface BridgeResult {\n /** Number of MCP prompts successfully registered or updated as skills. */\n bridgedCount: number;\n /**\n * Non-fatal bridge warnings or errors.\n * Static prompts may still bridge successfully while returning a fallback warning.\n */\n errors: Array<{ server: string; error: string }>;\n}\n\n// ============================================================================\n// MCPSkillBridge\n// ============================================================================\n\n/**\n * Bridges MCP server prompts into the skill registry as first-class skills.\n *\n * The bridge is designed to be called once per agent session, after\n * `mcpManager.connect()` resolves. It is idempotent — calling `bridge()`\n * multiple times updates previously bridged skills in place.\n */\nexport class MCPSkillBridge {\n private readonly mcpManager: MCPManager;\n private readonly skillRegistry: SkillRegistry;\n\n constructor(mcpManager: MCPManager, skillRegistry: SkillRegistry) {\n this.mcpManager = mcpManager;\n this.skillRegistry = skillRegistry;\n }\n\n /**\n * Discover all prompts from connected MCP servers and register them as skills.\n *\n * @returns Summary of what was bridged and any errors encountered.\n */\n async bridge(): Promise<BridgeResult> {\n const errors: BridgeResult[\"errors\"] = [];\n let bridgedCount = 0;\n\n let prompts: MCPPromptInfo[];\n try {\n prompts = await this.mcpManager.listPrompts();\n } catch (err) {\n // If listPrompts fails entirely, report but don't throw.\n errors.push({\n server: \"(all)\",\n error: err instanceof Error ? err.message : String(err),\n });\n return { bridgedCount, errors };\n }\n\n for (const prompt of prompts) {\n try {\n const { metadata, content, warning } =\n await this.buildPromptSkill(prompt);\n const existing = this.skillRegistry.get(metadata.name);\n this.skillRegistry.register(metadata, content);\n if (canRegisterExternalSkill(existing)) {\n bridgedCount++;\n }\n if (warning) {\n errors.push({\n server: prompt.server,\n error: warning,\n });\n }\n } catch (err) {\n errors.push({\n server: prompt.server,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { bridgedCount, errors };\n }\n\n // ==========================================================================\n // Internal\n // ==========================================================================\n\n private async buildPromptSkill(prompt: MCPPromptInfo): Promise<{\n metadata: SkillMetadata;\n content: SkillContent;\n warning?: string;\n }> {\n const metadata: SkillMetadata = {\n name: `${prompt.server}:${prompt.name}`,\n description:\n prompt.description ??\n `MCP prompt \"${prompt.name}\" from server \"${prompt.server}\".`,\n // MCP-bridged skills use \"global\" scope — local/project/user skills win.\n scope: \"global\",\n source: {\n type: \"remote\",\n root: `mcp://${prompt.server}`,\n },\n };\n\n if ((prompt.arguments?.length ?? 0) > 0) {\n return {\n metadata,\n content: {\n ...metadata,\n body: formatPromptTemplate(prompt),\n resources: [],\n },\n };\n }\n\n try {\n const result = await this.mcpManager.getPrompt(\n prompt.server,\n prompt.name,\n );\n\n return {\n metadata,\n content: {\n ...metadata,\n body: formatPromptMessages(prompt, result.messages),\n resources: [],\n },\n };\n } catch (err) {\n return {\n metadata,\n content: {\n ...metadata,\n body: formatPromptTemplate(prompt),\n resources: [],\n },\n warning:\n `Failed to render MCP prompt \"${prompt.name}\" from \"${prompt.server}\": ` +\n `${err instanceof Error ? err.message : String(err)}. ` +\n `Using prompt metadata fallback instead.`,\n };\n }\n }\n}\n\n// ============================================================================\n// Formatting helpers\n// ============================================================================\n\ntype PromptMessage = {\n role: string;\n content:\n | { type: string; text?: string }\n | Array<{ type: string; text?: string }>;\n};\n\n/**\n * Convert MCP prompt messages to a markdown skill body.\n *\n * Multiple messages are formatted with role headers so the agent\n * understands the expected interaction pattern when the prompt\n * represents a conversation template.\n */\nfunction formatPromptMessages(\n prompt: MCPPromptInfo,\n messages: PromptMessage[],\n): string {\n const lines = [`# MCP Prompt: ${prompt.server}/${prompt.name}`];\n\n if (prompt.description) {\n lines.push(\"\");\n lines.push(prompt.description);\n }\n\n if (messages.length === 0) return lines.join(\"\\n\");\n\n if (messages.length === 1 && messages[0]) {\n lines.push(\"\");\n lines.push(extractMessageText(messages[0]));\n return lines.join(\"\\n\");\n }\n\n lines.push(\"\");\n lines.push(\n messages\n .map((msg) => {\n const role =\n msg.role === \"user\"\n ? \"**User**\"\n : msg.role === \"assistant\"\n ? \"**Assistant**\"\n : `**${msg.role}**`;\n const text = extractMessageText(msg);\n return `${role}:\\n\\n${text}`;\n })\n .join(\"\\n\\n---\\n\\n\"),\n );\n\n return lines.join(\"\\n\");\n}\n\nfunction formatPromptTemplate(prompt: MCPPromptInfo): string {\n const lines = [`# MCP Prompt Template: ${prompt.server}/${prompt.name}`];\n\n if (prompt.description) {\n lines.push(\"\");\n lines.push(prompt.description);\n }\n\n const args = prompt.arguments ?? [];\n if (args.length > 0) {\n lines.push(\"\");\n lines.push(\"## Prompt Arguments\");\n for (const arg of args) {\n const required = arg.required ? \"required\" : \"optional\";\n lines.push(\n `- \\`${arg.name}\\` (${required})${arg.description ? `: ${arg.description}` : \"\"}`,\n );\n }\n lines.push(\"\");\n lines.push(\n \"This prompt is parameterized. A concrete prompt body depends on runtime argument values.\",\n );\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction extractMessageText(message: PromptMessage): string {\n if (Array.isArray(message.content)) {\n return message.content\n .map((part) =>\n part.type === \"text\" && part.text\n ? part.text\n : `[${part.type} content]`,\n )\n .join(\"\\n\\n\");\n }\n\n if (message.content.type === \"text\" && message.content.text) {\n return message.content.text;\n }\n return `[${message.content.type} content]`;\n}\n\nfunction canRegisterExternalSkill(\n existing: SkillMetadata | undefined,\n): boolean {\n if (!existing) {\n return true;\n }\n\n return !(\n existing.scope === \"builtin\" ||\n existing.source.type === \"local\" ||\n existing.filePath !== undefined ||\n existing.baseDir !== undefined\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,7 @@
1
+ export { C as CAPABILITY_PACK_KIND, a as CAPABILITY_PACK_SCHEMA_VERSION, b as CapabilityPack, G as GitPackRef, I as InlinePackRef, L as LocalPackRef, N as NpmPackRef, P as PackAuthor, c as PackMeta, d as PackRef, e as PackSkill, f as PackSkillResource, U as UrlPackRef, g as gitPack, i as inlinePack, l as localPack, n as npmPack, u as urlPack } from './types-BdapSdPT.js';
2
+ export { PackItemResult, PackLoadResult, PackLoader, PackLoaderOptions, defineCapabilityPack } from './pack/index.js';
3
+ export { resolvePackRef } from './sources/index.js';
4
+ export { BridgeResult, MCPSkillBridge } from './bridge/index.js';
5
+ import '@cuylabs/agent-core/mcp';
6
+ import '@cuylabs/agent-core/plugin';
7
+ import '@cuylabs/agent-core/skill';
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ import {
2
+ PackLoader,
3
+ defineCapabilityPack
4
+ } from "./chunk-IUGGMU63.js";
5
+ import {
6
+ gitPack,
7
+ inlinePack,
8
+ localPack,
9
+ npmPack,
10
+ urlPack
11
+ } from "./chunk-2TICMDUA.js";
12
+ import {
13
+ CAPABILITY_PACK_KIND,
14
+ CAPABILITY_PACK_SCHEMA_VERSION,
15
+ resolvePackRef
16
+ } from "./chunk-5E66GFAI.js";
17
+ import {
18
+ MCPSkillBridge
19
+ } from "./chunk-LDYHPSNE.js";
20
+ export {
21
+ CAPABILITY_PACK_KIND,
22
+ CAPABILITY_PACK_SCHEMA_VERSION,
23
+ MCPSkillBridge,
24
+ PackLoader,
25
+ defineCapabilityPack,
26
+ gitPack,
27
+ inlinePack,
28
+ localPack,
29
+ npmPack,
30
+ resolvePackRef,
31
+ urlPack
32
+ };
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}