| name | pack-builder |
| description | Author, validate, and build a Duo distro pack — a Claude Code plugin folder with a Duo `duo-extras/` subtree that ships an organization's skills + agents + canvases + CLAUDE.md guidance to a canonical signed Duo install. Use when a user says "build a distro pack for X", "make an AIP-flavored Duo", "package my team's skills as a Duo plugin", or "scaffold a new distro pack." Pairs with the duo-pack-service install pipeline (Stage 21d-i) and the `duo pack list / uninstall` CLI verbs. |
Pack-builder skill — Duo distro packs
Why this skill exists. A distro pack is the artifact a corporate
platform team / educational program / OSS community group ships to
end users so their canonical Duo install picks up org-specific
skills, agents, canvases, playgrounds, and CLAUDE.md guidance —
without forcing users to recompile Duo. Per Stage 21d (PRD at
docs/prd/stage-21d-distro-packs.md), the pack format is the
canonical Claude Code plugin layout (.claude-plugin/plugin.json
skills/<name>/SKILL.md + agents/<name>.md) plus a duo-extras/
subtree for Duo-specific content (canvases, playgrounds, integration
manifest, CLAUDE.md snippet, etc.). Distro builders shouldn't have
to remember the format from scratch every time — this skill is
the canonical authoring path.
What a distro pack contains
my-distro-pack/ ← the pack root (any name)
├── .claude-plugin/
│ └── plugin.json ← Claude Code plugin manifest (identity)
├── duo-extras/
│ └── DISTRO.json ← Duo integration manifest (FTUX + toggles)
│ └── claude-md-snippet.md ← (optional) merged into ~/.claude/CLAUDE.md
│ └── priming-additions.md ← (optional) appended to ~/.claude/duo/priming.md
│ └── external-domains.json ← (optional) merged into external-domains.json
│ └── canvases/ ← (optional) → ~/.claude/duo/distros/<name>/canvases/
│ └── playgrounds/ ← (optional) → ~/.claude/duo/distros/<name>/playgrounds/
│ └── canvas-templates/ ← (optional) → ~/.claude/duo/distros/<name>/canvas-templates/
├── skills/ ← installed as ~/.claude/skills/<distro>-<name>/
│ └── example-skill/
│ ├── SKILL.md ← required entrypoint per Claude Code plugin spec
│ └── (optional supporting files)
├── agents/ ← installed as ~/.claude/agents/<distro>-<name>.md
│ └── example-agent.md
└── (optional: hooks/, .mcp.json, .lsp.json — Stage 21d-i v2 routing)
A reference template ships at
examples/distro-pack-template/ in the
Duo source repo. Copy that folder; fill in the manifests; replace the
example skills/agents with your content; ship the result as a folder
the user (or IT) drops into ~/.claude/duo/extra-packs/<distro-name>/.
Naming rules (hard)
- Distro name (
.claude-plugin/plugin.json § name): lowercase
letters + numbers + hyphens only. Max 32 characters. This name
becomes the prefix on every install destination.
- Skill folder names (
skills/<name>/): same character set.
Combined <distro>-<skill> must be ≤64 characters (Claude Code
spec). The pack-builder warns at ≥56 chars (8-char buffer for
defensive headroom).
- Agent filenames (
agents/<name>.md): same character set on
the basename. Combined <distro>-<name>.md must be ≤64 characters.
These constraints are surfaced by pack-builder validate (below).
Authoring checklist (manual — agent walks it)
- Decide the distro name. Lowercase, kebab-case, ≤32 chars.
Example:
aip-corporate, course-bootcamp-2026,
foss-community-pack.
- Copy the template:
cp -r examples/distro-pack-template/ ~/Documents/<distro-name>/.
- Fill in
.claude-plugin/plugin.json:
name: the distro name from step 1.
version: semver ("1.0.0" for first release).
description: one-sentence what this pack contains and who it's
for.
author: { name, email } or just name.
homepage / repository / license: optional; helpful for
downstream discoverability.
- Fill in
duo-extras/DISTRO.json:
requiresDuoVersion: range (e.g. ">=0.6.7 <0.8"). HARD BLOCK
— Duo refuses to install a pack on an unsupported version.
openOnFirstLaunch: array of relative paths under duo-extras/
(canvases / playgrounds) that should auto-open as tabs on FIRST
detection of this pack. Re-fires once per pack-name.
pinnedFiles: array of { path, title } entries for the
navigator's pin list (first-install-only merge).
claudeMdSnippet / primingAdditions / externalDomainsAdditions:
booleans. Default to true if the corresponding file is present.
- Author skills under
skills/<name>/SKILL.md: standard Claude
Code skill format. The pack-builder validates the YAML frontmatter
(description present, name not overriding the distro prefix).
- Author agents under
agents/<name>.md: standard Claude Code
agent format. Same naming rules as skills.
- (Optional) Author Duo-specific content:
duo-extras/canvases/<file>.html — read-only HTML pages.
duo-extras/playgrounds/<file>.html — interactive HTML
pages (declare <meta name="duo-open-in" content="browser">
per the modality lock — see make-playground.md).
duo-extras/canvas-templates/<file>.html — reusable templates
for make-page / make-playground consumers.
duo-extras/claude-md-snippet.md — a managed block for
~/.claude/CLAUDE.md. Sits alongside Duo's own ENH-088 block;
never overrides it.
duo-extras/priming-additions.md — appended to
~/.claude/duo/priming.md.
duo-extras/external-domains.json — additive entries.
- Validate the pack with
pack-builder validate <pack-folder>
(see § "Validate an existing pack" below).
- Distribute by zipping the folder + posting to a download
location, or by mass-deploying via Jamf / Munki / similar to
~/.claude/duo/extra-packs/<distro-name>/. End users' Duo
install service picks the pack up on next launch.
Validate an existing pack
Run a structural + manifest check before distributing:
Validation steps the skill walks:
.claude-plugin/plugin.json exists + parses + has a valid name
field (lowercase + hyphens + numbers, ≤32 chars).
duo-extras/DISTRO.json exists OR is intentionally absent (a
manifestless pack is treated as defaults — claude-md-snippet etc.
auto-detected by file presence).
- Every
skills/<name>/ folder contains a SKILL.md (required by
Claude Code's plugin spec).
- Every
SKILL.md has a description field in frontmatter.
- Combined
<distro>-<skill> length ≤64 chars (warn at ≥56).
- Every agent file is a
.md with valid frontmatter.
requiresDuoVersion (if set) parses as >=A.B.C <X.Y.Z form
(the parser falls open on other forms with a warn).
openOnFirstLaunch paths resolve to actual files under
duo-extras/.
pinnedFiles paths resolve.
- No SKILL.md frontmatter sets
name explicitly without the
distro prefix (would defeat the namespacing convention).
Build distribution artifacts
Three distribution paths per the Stage 21d PRD:
Path 1 — Drop-in zip (Jamf / Munki / manual download)
cd ~/Documents/<distro-name>/..
zip -r <distro-name>-pack-<version>.zip <distro-name>/
End user (or IT) extracts the zip into ~/.claude/duo/extra-packs/.
Duo discovers + installs on next launch. Lowest packaging effort;
no signing.
Path 2 — .pkg installer (corporate IT, polished UX)
Path 3 — Fork + compile (early-adopter / pre-DMG-approval)
For companies whose security review hasn't blessed the upstream
signed DMG yet:
Update version
When you publish a new pack version:
- Bump
.claude-plugin/plugin.json § version (semver: patch / minor / major).
- Update the marker version in the pack's
claude-md-snippet.md
automatically — Duo's CLAUDE.md merge logic uses the version
in the manifest's marker, so there's nothing to edit by hand.
- Re-validate.
- Re-distribute (zip / .pkg / fork-merge).
End users' Duo install service detects the new version on next
launch (the install pipeline is atomic-replace: previous version's
tracked files are deleted before the new pack contents land). Owner
note: Duo never re-fires FTUX (openOnFirstLaunch) for an update —
only on first detection of a new pack name.
Cross-references