Skip to content

Skill Packs: bundle multiple skills into a single installable pack #4

@mgoldsborough

Description

@mgoldsborough

Summary

Add "skill packs" to mpak: a single registry entry that references multiple individual skills. When a user runs mpak skill install @scope/pack-name, mpak fetches the pack manifest, resolves the skill references, and installs each skill individually to ~/.claude/skills/. Claude Code never sees packs directly.

Motivation

Users building workflows often need a set of related skills that work together (e.g., a "DevOps pack" with k8s, docker, and terraform skills). Today they must install each skill individually and know which ones go together. Packs give authors a way to curate and distribute skill bundles.

Design Principles

  • Packs are NOT a separate entity. A pack is a skill with type='pack'. One table, one search, one install command. Users don't think about the distinction.
  • Version resolution at announce time. Pack versions store immutable snapshots of resolved skill versions.
  • Name collision prevention. Cannot publish a pack with the same name as a skill, or vice versa.
  • Tracking in ~/.mpak/packs/. mpak's data stays in mpak's directory, not Claude Code's.

User Stories

  1. Install a pack: mpak skill install @nimblebrain/devops fetches the pack manifest, resolves each skill reference, installs all skills to ~/.claude/skills/, writes a tracking file to ~/.mpak/packs/devops.json.
  2. Search returns both: mpak skill search devops shows individual skills and packs (with a type indicator and skill count). Optional --type pack filter.
  3. List shows membership: mpak skill list shows a PACK column indicating which pack (if any) each installed skill belongs to.
  4. Uninstall a pack: mpak skill uninstall @nimblebrain/devops removes all skills in the pack and the tracking file.
  5. Publish a pack: CI calls POST /v1/skills/announce-pack with a manifest. The registry validates all referenced skills exist and resolves semver ranges.

Pack Manifest (pack.json)

{
  "name": "@nimblebrain/devops",
  "version": "1.0.0",
  "description": "Essential DevOps skills for cloud infrastructure",
  "skills": {
    "@nimblebrain/k8s": "^1.0.0",
    "@nimblebrain/docker": "^2.1.0",
    "@nimblebrain/terraform": ">=1.0.0 <3.0.0"
  },
  "author": { "name": "NimbleBrain" },
  "keywords": ["devops", "cloud", "infrastructure"]
}

Scope

In scope

  • Zod schemas for pack manifest, announce request/response, tracking file
  • Database migration: type column on Skill, nullable artifact fields + pack fields on SkillVersion
  • Repository: type filter in search, pack field handling in upsert
  • API: POST /v1/skills/announce-pack endpoint, type filter on search, pack info on detail
  • SDK: type exports (existing getSkill() already returns enough)
  • CLI: install (pack detection + parallel skill install), uninstall, list (pack column)

Out of scope (deferred)

  • mpak skill update for packs
  • Pack dependency resolution (packs cannot depend on other packs)
  • Lockfile support
  • Pack-level configuration or environment variables

Implementation Sketch

Affected packages

Package Changes
packages/schemas New skill-pack.ts, extend skill.ts with type/skills fields
apps/registry Migration (1 new col on Skill, 3 nullable + 3 new on SkillVersion), repository updates, new announce-pack route, search/detail type awareness
packages/sdk Re-export new types
packages/cli Extract reusable installSingleSkill(), new install-pack.ts, new uninstall.ts, list pack column, register uninstall command

Estimate: 4 new files, ~13 modified files.

Database changes

Skill table: add type VARCHAR(20) DEFAULT 'skill'

SkillVersion table:

  • Make nullable: storage_path, digest, size_bytes (packs have no S3 artifact)
  • Add: pack_manifest JSON, resolved_skills JSON, skill_count INT DEFAULT 0

Key API behavior

  • POST /v1/skills/announce-pack: validates manifest, resolves all skill semver ranges against registry, stores resolved snapshot, rejects if any skill missing
  • GET /v1/skills/search?type=pack: optional type filter
  • GET /v1/skills/@scope/name: includes type and skills[] array for packs
  • Name collision: announce-pack rejects if name already exists as type='skill', and vice versa

CLI behavior

  • mpak skill install @scope/pack: detects type='pack' from detail response, installs each skill in parallel via Promise.allSettled, writes tracking file
  • mpak skill uninstall @scope/pack: reads tracking file, removes all skills, deletes tracking file
  • mpak skill uninstall @scope/skill: removes single skill, updates any referencing tracking files

Verification

pnpm typecheck && pnpm lint && pnpm test && pnpm build

Manual smoke tests:

  • Search returns both types with type indicator
  • Install pack installs all skills + writes tracking file
  • List shows pack membership
  • Uninstall pack removes all skills + tracking file

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions