[codex] Add skills CLI and catalog management (#6782)

## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies through
company-scoped control-plane workflows.
> - Agents need reusable, inspectable skills that can be installed,
reset, audited, exported, and assigned without bespoke local setup.
> - The existing skill truth model needed cleanup so bundled skills,
optional catalog skills, runtime skills, and adapter-provided skills
have clear provenance.
> - Operators also need a practical CLI and board UI for discovering and
managing company skills.
> - This pull request adds the skills CLI, packaged skills catalog,
company skills APIs, and catalog-aware board UI.
> - The benefit is a more reusable Paperclip company setup where skills
are portable, auditable, and easier for operators and agents to manage.

## What Changed

- Added `paperclipai skills` CLI commands and coverage for catalog
listing, installing, resetting, and inspecting company skills.
- Added a packaged `@paperclipai/skills-catalog` workspace with bundled
and optional skill content plus validation/build tests.
- Added shared company-skill types and validators used across CLI,
server, and UI contracts.
- Added server catalog APIs/services for company skill catalog
operations, reset semantics, audit behavior, and portability provenance.
- Updated adapter skill handling so runtime/catalog provenance remains
explicit across local adapters.
- Added board UI support for browsing and managing catalog-backed
company skills.
- Updated docs for the skills CLI/catalog flow and the company skills
Paperclip skill reference.
- Rebased the branch onto current `paperclipai/paperclip:master`; no
`pnpm-lock.yaml`, `.github/workflows`, or migration files are included
in the final PR diff.

## Verification

- Passed: `pnpm run preflight:workspace-links && pnpm exec vitest run
cli/src/__tests__/skills.test.ts
packages/skills-catalog/src/catalog-builder.test.ts
packages/skills-catalog/src/shipped-catalog.test.ts
packages/shared/src/validators/company-skill.test.ts
packages/adapter-utils/src/server-utils.test.ts
packages/plugins/create-paperclip-plugin/src/entrypoints.test.ts
server/src/__tests__/company-skills-catalog-service.test.ts
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/company-portability.test.ts`.
- Passed: `pnpm exec vitest run
server/src/__tests__/workspace-runtime.test.ts -t "default
branch|origin/master|symbolic-ref"`.
- Attempted: full `server/src/__tests__/workspace-runtime.test.ts`. Four
provisioning tests failed while seeding an isolated worktree database
from the local Paperclip instance because the local plugin schema dump
contains a duplicate-column foreign key
(`plugin_content_machine_18a7bc327b.content_case_signals`). The
default-branch tests touched by the rebase conflict passed in the
focused run above.
- Checked final diff: no `pnpm-lock.yaml`, no `.github/workflows`, and
no migration-file changes relative to `master`.

## Risks

- Medium: this is a broad skills/catalog change touching CLI, server
APIs, shared contracts, adapter skill sync, and UI.
- Catalog validation and reset semantics need careful reviewer attention
because they affect reusable company setup and portability.
- No database migrations are included in this PR, so there is no
migration ordering/idempotency risk in the final diff.
- No lockfile is included by design; dependency resolution will be
handled by the repository lockfile workflow.

## Model Used

- OpenAI Codex coding agent based on GPT-5, running in Paperclip via the
`codex_local` adapter with shell, git, GitHub CLI, and code-editing tool
access. Exact hosted model build/context-window metadata is not exposed
in this runtime.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run targeted tests locally and documented the local
workspace-runtime seed failure above
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, screenshots were intentionally
omitted per PAP-10124 instructions; UI behavior is covered by tests and
reviewer inspection
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta 2026-05-28 07:33:51 -10:00 committed by GitHub
parent 8da50dbcf8
commit 9eac727cf1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
77 changed files with 9704 additions and 530 deletions

View file

@ -143,6 +143,124 @@ pnpm paperclipai agent local-cli codexcoder --company-id <company-id>
pnpm paperclipai agent local-cli claudecoder --company-id <company-id>
```
## Skills Commands
`paperclipai skills` covers three distinct operations:
1. **Company install** — adds or updates a row in `company_skills` for the
whole company. This is what `skills install`, `skills import`, `skills create`,
and `skills scan-projects` do.
2. **Agent attach** — replaces an agent's *desired* company skill set
(`skills agent sync`/`clear`). This is a desired-state operation on the
agent's adapter config; it does not change the company library.
3. **Adapter runtime sync** — the adapter reconciles the desired skill set
with files on disk and reports an `AgentSkillSnapshot` (`skills agent list`).
`skills agent sync` triggers this automatically after updating desired state.
Required Paperclip runtime skills (heartbeat, etc.) remain server-enforced and
are added on top of whatever the desired set names.
### Catalog (app-shipped skills)
The Paperclip app ships a curated catalog under `@paperclipai/skills-catalog`.
Browse and inspect commands never mutate company state; `install` adds a catalog
skill to the company library.
```sh
pnpm paperclipai skills browse [--kind bundled|optional] [--category <slug>] [--query <text>]
pnpm paperclipai skills search "<text>" [--kind bundled|optional] [--category <slug>]
pnpm paperclipai skills inspect <catalog-id-or-key-or-slug>
pnpm paperclipai skills install <catalog-id-or-key-or-slug> [--as <slug>] [--force] --company-id <company-id>
```
Catalog semantics:
- **Bundled** skills live in `packages/skills-catalog/catalog/bundled/<category>/<slug>`
and are recommended defaults for most companies. They use canonical key
`paperclipai/bundled/<category>/<slug>`.
- **Optional** skills live in `packages/skills-catalog/catalog/optional/<category>/<slug>`
and are role-specific or domain-specific (browser, AWS ops, etc.). Same key
shape with `optional` in place of `bundled`.
- `skills install` materializes the catalog files into a company-managed skill
directory and records provenance (`catalogId`, `catalogKey`, `packageVersion`,
`originHash`, …) so future updates and audit decisions stay consistent.
- `--as <slug>` overrides the company skill slug. `--force` may replace a
same-key catalog-managed skill but never bypasses hard validation or hard-stop
audit findings.
Examples:
```sh
pnpm paperclipai skills browse --kind bundled --company-id <company-id>
pnpm paperclipai skills search "pull request" --kind bundled
pnpm paperclipai skills inspect github-pr-workflow
pnpm paperclipai skills install github-pr-workflow --company-id <company-id>
pnpm paperclipai skills install paperclipai:optional:browser:agent-browser --company-id <company-id>
```
External GitHub, skills.sh, local-path, and URL sources still go through
`skills import`; catalog commands are for the app-shipped catalog only.
### Company library
```sh
pnpm paperclipai skills list --company-id <company-id>
pnpm paperclipai skills show <skill-id-or-key-or-slug> --company-id <company-id>
pnpm paperclipai skills file <skill-id-or-key-or-slug> [--path SKILL.md] --company-id <company-id>
pnpm paperclipai skills import <source> --company-id <company-id>
pnpm paperclipai skills create --name "Review PRs" [--slug review-prs] [--description "..."] [--body-file SKILL.md] --company-id <company-id>
pnpm paperclipai skills scan-projects [--project-id <id>...] [--workspace-id <id>...] --company-id <company-id>
pnpm paperclipai skills check [skill-id-or-key-or-slug] --company-id <company-id>
pnpm paperclipai skills update <skill-id-or-key-or-slug> [--force] --company-id <company-id>
pnpm paperclipai skills update --all [--force] --company-id <company-id>
pnpm paperclipai skills audit [skill-id-or-key-or-slug] --company-id <company-id>
pnpm paperclipai skills reset <skill-id-or-key-or-slug> [--yes] [--force] --company-id <company-id>
pnpm paperclipai skills remove <skill-id-or-key-or-slug> --yes --company-id <company-id>
```
`skills import <source>` accepts a skills.sh URL, the equivalent
`<owner>/<repo>/<skill>` shorthand, a GitHub URL, a local path, or an
`npx skills add …` command. See `references/company-skills.md` in the agent
skill bundle for the source-type table.
`skills check`, `skills update`, `skills audit`, and `skills reset` are the
maintenance loop for catalog-installed skills:
- `check` reports whether each skill's installed bytes match its pinned origin
(`hasUpdate`, `installedHash`, `originHash`, `updateHoldReason`,
`auditVerdict`).
- `update` installs the pinned update through the existing install-update API.
`--all` checks every company skill and updates only those with
`hasUpdate=true`. `--force` discards local-modification or soft-audit holds;
hard-stop audit findings still block the update.
- `audit` re-scans installed bytes and reports findings without executing
anything.
- `reset` reinstalls a catalog-managed skill from its pinned origin, discarding
local edits. Prompts in a TTY; requires `--yes` for non-interactive use.
### Agent attach
```sh
pnpm paperclipai skills agent list <agent-id-or-shortname> --company-id <company-id>
pnpm paperclipai skills agent sync <agent-id-or-shortname> --skill <skill-id-or-key-or-slug> [--skill <skill-id-or-key-or-slug>...] --company-id <company-id>
pnpm paperclipai skills agent clear <agent-id-or-shortname> --yes --company-id <company-id>
```
`skills agent sync` replaces the agent's non-required desired skill set (it is
not additive) and returns the resulting adapter `AgentSkillSnapshot`.
`skills agent clear` sends an empty desired list. Required Paperclip skills are
still enforced by the server in both cases.
### Notes
- Skill references accept company skill `id`, canonical `key`, or unique
`slug`; catalog references accept catalog `id`, `key`, or unique `slug`.
- `skills file` prints raw file content in human mode so it can be piped.
- `skills create --body-file -` reads the skill markdown body from stdin.
- `skills remove`, `skills reset`, and `skills agent clear` prompt in a TTY and
require `--yes` in non-interactive use.
- `--json` prints the raw API result for each command.
## Secrets Commands
```sh

View file

@ -420,6 +420,62 @@ eval "$(pnpm paperclipai worktree env)"
For project execution worktrees, Paperclip can also run a project-defined provision command after it creates or reuses an isolated git worktree. Configure this on the project's execution workspace policy (`workspaceStrategy.provisionCommand`). The command runs inside the derived worktree and receives `PAPERCLIP_WORKSPACE_*`, `PAPERCLIP_PROJECT_ID`, `PAPERCLIP_AGENT_ID`, and `PAPERCLIP_ISSUE_*` environment variables so each repo can bootstrap itself however it wants.
## App-Shipped Skills Catalog
The Paperclip app ships a curated catalog of company skills out of the box. The
catalog is a workspace package at `packages/skills-catalog`:
```text
packages/skills-catalog/
catalog/
bundled/<category>/<slug>/SKILL.md # recommended defaults
optional/<category>/<slug>/SKILL.md # role/domain-specific
generated/catalog.json # checked-in manifest
scripts/
build-catalog-manifest.ts # regenerate generated/catalog.json
validate-catalog.ts # validation only
src/ # builder + types consumed by server/CLI
```
Server and CLI import the generated manifest; they do not crawl repository
paths at request time. Root `skills/` remains reserved for Paperclip runtime
skills and is not part of the catalog.
Validate the catalog without writing the manifest:
```sh
pnpm --filter @paperclipai/skills-catalog validate
```
Regenerate `generated/catalog.json` after editing any catalog `SKILL.md`,
frontmatter, file inventory, category, or slug:
```sh
pnpm --filter @paperclipai/skills-catalog build:manifest
```
The package's `build` script runs `build:manifest` and then `tsc`; tests live
under `pnpm --filter @paperclipai/skills-catalog test`. Validation fails when:
- a catalog entry is not under `catalog/bundled/<category>/<slug>` or
`catalog/optional/<category>/<slug>`
- `SKILL.md` is missing or the frontmatter `name`/`description` is empty
- the frontmatter `key` disagrees with the generated canonical key
- two catalog entries share an `id`, `key`, or `slug`
- file inventory contains absolute paths, `..`, broken symlinks, or files
outside the skill directory
- the regenerated manifest differs from the checked-in
`generated/catalog.json`
Trust level is derived from inventory: `markdown_only` (markdown + references
only), `assets` (other non-script files), or `scripts_executables` (any
executable script). The build contract is documented in
`doc/plans/2026-05-26-skills-cli-catalog-contract.md`.
CI runs `pnpm --filter @paperclipai/skills-catalog validate` and the package's
vitest suite, so always regenerate the manifest in the same commit as the
catalog change.
## Quick Health Checks
In another terminal:

View file

@ -1,6 +1,6 @@
# 2026-03-14 Adapter Skill Sync Rollout
Status: Proposed
Status: Implemented for local adapters; gateway remains unsupported
Date: 2026-03-14
Audience: Product and engineering
Related:
@ -25,8 +25,10 @@ Paperclip currently has these adapters:
- `claude_local`
- `codex_local`
- `cursor_local`
- `cursor`
- `gemini_local`
- `grok_local`
- `acpx_local`
- `opencode_local`
- `pi_local`
- `openclaw_gateway`
@ -39,12 +41,14 @@ The current skill API supports:
Current implementation state:
- `codex_local`: implemented, `persistent`
- `codex_local`: implemented, `ephemeral`
- `claude_local`: implemented, `ephemeral`
- `cursor_local`: not yet implemented, but technically suited to `persistent`
- `gemini_local`: not yet implemented, but technically suited to `persistent`
- `pi_local`: not yet implemented, but technically suited to `persistent`
- `opencode_local`: not yet implemented; likely `persistent`, but with special handling because it currently injects into Claudes shared skills home
- `cursor`: implemented, `persistent`
- `gemini_local`: implemented, `persistent`
- `pi_local`: implemented, `persistent`
- `opencode_local`: implemented, `persistent`, with shared Claude skills home caveats
- `acpx_local`: implemented, `ephemeral` for Claude/Codex sub-agents and `unsupported` for custom commands
- `grok_local`: implemented, `ephemeral`
- `openclaw_gateway`: not yet implemented; blocked on gateway protocol support, so `unsupported` for now
## 3. Product Principles
@ -64,8 +68,7 @@ These adapters have a stable local skills directory that Paperclip can read and
Candidates:
- `codex_local`
- `cursor_local`
- `cursor`
- `gemini_local`
- `pi_local`
- `opencode_local` with caveats
@ -84,7 +87,10 @@ These adapters do not have a meaningful Paperclip-owned persistent install state
Current adapter:
- `codex_local`
- `claude_local`
- `acpx_local` when configured for Claude or Codex
- `grok_local`
Expected UX:
@ -99,6 +105,7 @@ These adapters cannot support skill sync without new external capabilities.
Current adapter:
- `acpx_local` when configured for custom commands
- `openclaw_gateway`
Expected UX:
@ -114,7 +121,7 @@ Expected UX:
Target mode:
- `persistent`
- `ephemeral`
Current state:
@ -122,15 +129,15 @@ Current state:
Requirements to finish:
- keep as reference implementation
- tighten tests around external custom skills and stale removal
- ensure imported company skills can be attached and synced without manual path work
- keep runtime-mounted snapshots separate from persistent install snapshots
- ensure imported company skills can be attached and mounted without manual path work
- keep `CODEX_HOME/skills` mutation scoped to heartbeat execution, not `skills/sync`
Success criteria:
- list installed managed and external skills
- sync desired skills into `CODEX_HOME/skills`
- preserve external user-managed skills
- desired skills are stored in Paperclip
- selected skills are linked into the effective `CODEX_HOME/skills` during runs
- no persistent installed/stale state is reported from `skills/sync`
### 5.2 Claude Local
@ -162,18 +169,11 @@ Target mode:
Technical basis:
- runtime already injects Paperclip skills into `~/.cursor/skills`
- Paperclip reconciles desired skills into `~/.cursor/skills`
Implementation work:
Current state:
1. Add `listSkills` for Cursor.
2. Add `syncSkills` for Cursor.
3. Reuse the same managed-symlink pattern as Codex.
4. Distinguish:
- managed Paperclip skills
- external skills already present
- missing desired skills
- stale managed skills
- implemented
Testing:
@ -194,14 +194,11 @@ Target mode:
Technical basis:
- runtime already injects Paperclip skills into `~/.gemini/skills`
- Paperclip reconciles desired skills into `~/.gemini/skills`
Implementation work:
Current state:
1. Add `listSkills` for Gemini.
2. Add `syncSkills` for Gemini.
3. Reuse managed-symlink conventions from Codex/Cursor.
4. Verify auth remains untouched while skills are reconciled.
- implemented
Potential caveat:
@ -219,14 +216,11 @@ Target mode:
Technical basis:
- runtime already injects Paperclip skills into `~/.pi/agent/skills`
- Paperclip reconciles desired skills into `~/.pi/agent/skills`
Implementation work:
Current state:
1. Add `listSkills` for Pi.
2. Add `syncSkills` for Pi.
3. Reuse managed-symlink helpers.
4. Verify session-file behavior remains independent from skill sync.
- implemented
Success criteria:
@ -250,9 +244,7 @@ This is product-risky because:
Plan:
Phase 1:
- implement `listSkills` and `syncSkills`
- implemented `listSkills` and `syncSkills`
- treat it as `persistent`
- explicitly label the home as shared in UI copy
- only remove stale managed Paperclip skills that are clearly marked as Paperclip-managed
@ -290,6 +282,30 @@ Future target:
- likely a fourth truth model eventually, such as remote-managed persistent state
- for now, keep the current API and treat gateway as unsupported
### 5.8 ACPX Local
Target mode:
- `ephemeral` for built-in Claude/Codex ACPX sub-agents
- `unsupported` for custom ACP commands
Success criteria:
- Claude/Codex ACPX snapshots show skills as configured for the next session
- custom command snapshots keep desired skills tracked only and do not imply runtime sync
### 5.9 Grok Local
Target mode:
- `ephemeral`
Success criteria:
- desired skills are stored in Paperclip
- selected skills are copied into the execution workspace for the next run
- no persistent installed/stale state is reported from `skills/sync`
## 6. API Plan
## 6.1 Keep the current minimal adapter API
@ -333,14 +349,13 @@ Additional UI requirement for shared-home adapters:
Ship:
- `cursor_local`
- `cursor`
- `gemini_local`
- `pi_local`
Rationale:
Status:
- these are the closest to Codex in architecture
- they already inject into stable local skill homes
- implemented
### Phase 2: OpenCode shared-home support
@ -348,10 +363,9 @@ Ship:
- `opencode_local`
Rationale:
Status:
- technically feasible now
- needs slightly more careful product language because of the shared Claude skills home
- implemented with shared Claude skills-home warning
### Phase 3: Gateway support decision
@ -390,10 +404,10 @@ Adapter-wide skill support is ready when all are true:
The recommended immediate order is:
1. `cursor_local`
1. `cursor`
2. `gemini_local`
3. `pi_local`
4. `opencode_local`
5. defer `openclaw_gateway`
That gets Paperclip from “skills work for Codex and Claude” to “skills work for the whole local-adapter family,” which is the meaningful V1 milestone.
The local-adapter family now has explicit truth models. The remaining V1 boundary is `openclaw_gateway`, which should stay unsupported until the gateway protocol can report real remote skill state.

View file

@ -0,0 +1,486 @@
# Skills CLI And Catalog Contract
Status: Phase A engineering contract
Date: 2026-05-26
Source plan: approved Paperclip skills CLI and catalog plan
This document freezes the first implementation contract for the `paperclipai skills`
command group and the app-shipped skills catalog. It is intentionally a build
contract, not a full product spec.
## Decisions
- `paperclipai skills` manages Paperclip company skills. It does not manage
local adapter homes directly.
- Installing a skill means adding or updating a company-scoped
`company_skills` record.
- Attaching a skill to an agent is a separate agent desired-state operation.
- Adapter runtime sync is a third step handled through adapter skill APIs.
- Root `skills/` remains reserved for Paperclip runtime and operational skills.
- App-shipped catalog skills live in `packages/skills-catalog`, not root
`skills/`.
- Catalog skills are inspectable before install. Inspection never mutates company
state.
- External sources continue to use the existing company skill import API in the
first release. No separate marketplace, tap, or source registry is part of this
phase.
- Agent desired skills continue to live in
`adapterConfig.paperclipSkillSync.desiredSkills` for the first release. Do not
add a normalized `agent_skills` table unless later implementation evidence
requires it.
## Terms
- Company skill: a row in `company_skills`, owned by one company.
- Catalog skill: an app-shipped skill entry in `@paperclipai/skills-catalog`.
- Skill ref: a user-supplied company skill reference. The CLI accepts company
skill `id`, canonical `key`, or unique `slug`.
- Catalog ref: a user-supplied catalog reference. The CLI accepts catalog `id`,
canonical `key`, or unique `slug`.
- Desired skills: the skill key set stored on the agent adapter config.
- Runtime snapshot: the adapter-reported `AgentSkillSnapshot` for desired,
installed, missing, stale, external, required, or unsupported skills.
## CLI Contract
All skills commands use the existing client command stack:
- Global client options: `--data-dir`, `--config`, `--context`, `--profile`,
`--api-base`, `--api-key`, and `--json`.
- Company-scoped commands also accept `-C, --company-id <id>` and otherwise use
`PAPERCLIP_COMPANY_ID` or the active context profile.
- Human output goes to stdout. Errors go to stderr.
- `--json` prints pretty JSON and no decorative labels.
- Successful commands exit `0`. Validation, API, or conflict errors exit `1`.
- API errors use the existing `API error <status>: <message>` formatting.
- Mutating commands print a short summary in human mode and the raw result in
JSON mode.
- Commands that can delete or clear state must prompt in a TTY. In non-TTY mode
they must require `--yes`.
### Company Skill Commands
These commands are Phase B and must work over existing APIs.
| Command | Behavior | JSON output |
|---|---|---|
| `skills list` | Lists company skills from `GET /api/companies/:companyId/skills`. Human rows include `id`, `key`, `slug`, `name`, `source`, `trust`, `compatibility`, and `attachedAgents`. | `CompanySkillListItem[]` |
| `skills show <skill-ref>` | Resolves `id`, `key`, or unique `slug`, then reads detail. Ambiguous slugs are conflicts. | `CompanySkillDetail` |
| `skills file <skill-ref> [--path <path>]` | Resolves the skill, reads a file with default `SKILL.md`, and prints raw file content in human mode. This command must remain pipeable. | `CompanySkillFileDetail` |
| `skills import <source>` | Calls existing import API. Source may be a local path, GitHub URL, skills.sh URL or command, `owner/repo`, `owner/repo/skill`, or URL-like source already accepted by the server. | `CompanySkillImportResult` |
| `skills create --name <name> [--slug <slug>] [--description <text>] [--body-file <path|->]` | Creates a managed local company skill. If `--body-file` is omitted, the server default body is used. `-` reads markdown from stdin. | `CompanySkill` |
| `skills scan-projects [--project-id <id>...] [--workspace-id <id>...]` | Calls project scan. Repeated flags become arrays. With neither flag, scan all accessible project workspaces. | `CompanySkillProjectScanResult` |
| `skills check [skill-ref]` | Reads update status for one skill, or for every listed company skill when no ref is provided. Unsupported statuses are shown, not hidden. | `CompanySkillCheckRow[]` |
| `skills update <skill-ref>` | Installs the update for one skill through the existing install-update API. | `CompanySkillUpdateRow` |
| `skills update --all` | Checks all skills, installs only those with `hasUpdate=true`, and reports skipped unsupported or current skills. | `CompanySkillUpdateRow[]` |
| `skills remove <skill-ref> [--yes]` | Deletes one company skill after confirmation. | `CompanySkill` |
`CompanySkillCheckRow` is a CLI-side shape:
```ts
interface CompanySkillCheckRow {
skill: Pick<CompanySkillListItem, "id" | "key" | "slug" | "name">;
status: CompanySkillUpdateStatus;
}
```
`CompanySkillUpdateRow` is a CLI-side shape:
```ts
interface CompanySkillUpdateRow {
skillRef: string;
action: "updated" | "skipped" | "failed";
skill?: CompanySkill;
status?: CompanySkillUpdateStatus;
reason?: string;
}
```
### Agent Skill Commands
These commands are Phase B and use existing agent skill APIs.
| Command | Behavior | JSON output |
|---|---|---|
| `skills agent list <agent-ref>` | Resolves the agent using existing agent reference behavior, then prints the adapter `AgentSkillSnapshot`. Human rows include `key`, `runtimeName`, `desired`, `managed`, `required`, `state`, `origin`, and `detail`. | `AgentSkillSnapshot` |
| `skills agent sync <agent-ref> --skill <skill-ref>...` | Replaces the agent's non-required desired skill set with the supplied refs and triggers adapter sync. Required Paperclip skills remain enforced by the server. | `AgentSkillSnapshot` |
| `skills agent clear <agent-ref> [--yes]` | Clears non-required desired skills by sending an empty desired list, then returns the adapter snapshot. | `AgentSkillSnapshot` |
The word `sync` is deliberate: it is a desired-state replacement, not an append.
An additive command can be added later if operators need it.
### Catalog CLI Commands
These commands are Phase E and depend on the catalog APIs from Phase D.
| Command | Behavior | JSON output |
|---|---|---|
| `skills browse [--kind bundled|optional] [--category <slug>] [--query <text>]` | Lists app-shipped catalog skills. Human rows include `id`, `key`, `kind`, `category`, `slug`, `name`, `trust`, and `recommendedForRoles`. | `CatalogSkillListItem[]` |
| `skills search <query> [--kind bundled|optional] [--category <slug>]` | Alias for catalog browse with `query`. | `CatalogSkillListItem[]` |
| `skills inspect <catalog-ref>` | Shows app-shipped catalog detail and file inventory. Does not mutate company state. | `CatalogSkillDetail` |
| `skills install <catalog-ref> [--as <slug>] [--force]` | Installs a catalog skill into a company library. `--as` overrides the company skill slug. `--force` may replace a same-key catalog skill but must not bypass hard validation or dangerous security findings. | `CompanySkillInstallCatalogResult` |
Catalog commands are for the app-shipped Paperclip catalog only. External GitHub,
skills.sh, local path, and URL installs remain under `skills import <source>` in
the first release.
## Catalog Package Contract
Add a workspace package:
```text
packages/skills-catalog/
package.json
tsconfig.json
src/
index.ts
types.ts
catalog/
bundled/
<category>/
<slug>/
SKILL.md
references/
scripts/
assets/
optional/
<category>/
<slug>/
SKILL.md
references/
scripts/
assets/
generated/
catalog.json
scripts/
build-catalog-manifest.ts
validate-catalog.ts
```
Package name: `@paperclipai/skills-catalog`.
The package exports:
- `catalogManifest`
- `catalogSkills`
- `resolveCatalogSkillRef(ref)`
- `getCatalogSkill(id)`
- TypeScript types for every manifest shape
Server and CLI code must import the generated manifest. They must not crawl
arbitrary repository paths at request time.
## Catalog Manifest
The generated artifact is `packages/skills-catalog/generated/catalog.json`.
It is checked in and regenerated by the package build or validation script.
```ts
interface CatalogManifest {
schemaVersion: 1;
packageName: "@paperclipai/skills-catalog";
packageVersion: string;
generatedAt: string;
skills: CatalogSkill[];
}
interface CatalogSkill {
id: string;
key: string;
kind: "bundled" | "optional";
category: string;
slug: string;
name: string;
description: string;
path: string;
entrypoint: "SKILL.md";
trustLevel: "markdown_only" | "assets" | "scripts_executables";
compatibility: "compatible" | "unknown" | "invalid";
defaultInstall: boolean;
recommendedForRoles: string[];
requires: string[];
tags: string[];
files: CatalogSkillFile[];
contentHash: string;
}
interface CatalogSkillFile {
path: string;
kind: "skill" | "markdown" | "reference" | "script" | "asset" | "other";
sizeBytes: number;
sha256: string;
}
```
`id` is path-safe:
```text
paperclipai:<kind>:<category>:<slug>
```
`key` is the canonical company skill key installed into `company_skills`:
```text
paperclipai/<kind>/<category>/<slug>
```
Example:
```json
{
"id": "paperclipai:bundled:software-development:github-pr-workflow",
"key": "paperclipai/bundled/software-development/github-pr-workflow",
"kind": "bundled",
"category": "software-development",
"slug": "github-pr-workflow",
"name": "github-pr-workflow",
"description": "Prepare pull requests, review responses, and verification notes.",
"path": "catalog/bundled/software-development/github-pr-workflow",
"entrypoint": "SKILL.md",
"trustLevel": "markdown_only",
"compatibility": "compatible",
"defaultInstall": false,
"recommendedForRoles": ["engineer"],
"requires": [],
"tags": ["github", "pull-requests"],
"files": [
{
"path": "SKILL.md",
"kind": "skill",
"sizeBytes": 1200,
"sha256": "..."
}
],
"contentHash": "sha256:..."
}
```
## Catalog Skill Frontmatter
Each catalog `SKILL.md` must include:
```yaml
---
name: github-pr-workflow
description: Prepare pull requests, review responses, and verification notes.
key: paperclipai/bundled/software-development/github-pr-workflow
recommendedForRoles:
- engineer
tags:
- github
- pull-requests
---
```
Optional frontmatter:
- `slug`
- `defaultInstall`
- `requires`
- `metadata`
The manifest generator owns `kind`, `category`, `path`, `files`,
`trustLevel`, `compatibility`, and `contentHash`.
## Catalog Validation Rules
Validation must fail when:
- A catalog entry is not under `catalog/bundled/<category>/<slug>` or
`catalog/optional/<category>/<slug>`.
- `SKILL.md` is missing.
- `category` or `slug` is not a lowercase URL slug.
- `name` or `description` frontmatter is missing or empty.
- The frontmatter `key`, when present, does not equal the generated key.
- Two catalog entries have the same `id`, `key`, or `slug`.
- File inventory includes absolute paths, `..` segments, broken symlinks, or
files outside the skill directory.
- A file exceeds the package-level size limit chosen by implementation.
- A skill marked `compatible` cannot be parsed as Agent Skills markdown.
- The generated manifest differs from the checked-in
`generated/catalog.json`.
Trust level is derived from inventory:
- `scripts_executables` when any file is classified as `script`.
- `assets` when any file is classified as `asset` or `other` and no script is
present.
- `markdown_only` when all files are markdown, references, or `SKILL.md`.
Validation must report all discovered catalog errors when practical, not just
the first one.
## Catalog API Contract
Phase D adds read APIs and one company install API.
```text
GET /api/skills/catalog
GET /api/skills/catalog/:catalogId
GET /api/skills/catalog/:catalogId/files?path=SKILL.md
POST /api/companies/:companyId/skills/install-catalog
```
`GET /api/skills/catalog` accepts:
- `kind=bundled|optional`
- `category=<slug>`
- `q=<text>`
`catalogId` is the path-safe manifest `id`. The server should also support
resolution by `key` or unique `slug` where the ref is carried in a query or body,
but route parameters use `id` to avoid slash handling ambiguity.
Install request:
```ts
interface CompanySkillInstallCatalogRequest {
catalogSkillId: string;
slug?: string | null;
force?: boolean;
}
```
Install result:
```ts
interface CompanySkillInstallCatalogResult {
action: "created" | "updated" | "unchanged";
skill: CompanySkill;
catalogSkill: CatalogSkill;
warnings: string[];
}
```
Install behavior:
- Creates or updates a company skill with `sourceType="catalog"`.
- Uses catalog `key` as the company skill canonical key.
- Uses catalog `slug` unless `slug` is provided.
- Materializes the catalog files into a company-managed skill directory so
existing skill file reads continue to work.
- Stores provenance in metadata:
- `catalogId`
- `catalogKey`
- `catalogKind`
- `catalogCategory`
- `catalogPath`
- `packageName`
- `packageVersion`
- `originHash`
- `originVersion`
- `userModifiedAt`
- `updateHoldReason`
- Writes activity log entries for install and update.
- Returns `409` for duplicate slug/key conflicts that cannot be resolved safely.
- Returns `422` for invalid, incompatible, or hard-blocked catalog entries.
- `force` may replace a same-key catalog-managed skill. It must not bypass
company boundaries, permission checks, hard validation, or hard security
findings.
## Error Semantics
Use existing HTTP semantics:
- `400`: invalid CLI arguments, invalid query/body shape, or malformed refs.
- `401`: missing or invalid auth.
- `403`: authenticated principal lacks access or mutation permission.
- `404`: skill, catalog entry, agent, file, company, or source not found.
- `409`: ambiguous slug, duplicate key/slug, update conflict, or unsafe overwrite.
- `422`: semantic violation such as invalid skill content or unsupported source.
- `500`: unexpected server failure.
CLI messages should name the next useful correction, for example:
- `Skill slug "review" is ambiguous. Use an id or key.`
- `Company ID is required. Pass --company-id, set PAPERCLIP_COMPANY_ID, or set a context profile.`
- `Catalog skill contains executable scripts and cannot be force-installed until security review semantics allow it.`
## Phase Acceptance Criteria
Phase A is complete when this contract is available in the repo and the issue
thread links it.
Phase B, CLI MVP:
- `paperclipai skills --help` exposes the Phase B command group.
- All Phase B commands work against existing company skills and agent skills
APIs without schema or server changes.
- Skill refs resolve by id, key, or unique slug.
- Human and JSON output are covered by focused CLI tests.
- `doc/CLI.md` documents company install vs agent desired sync vs runtime sync.
Phase C, catalog package:
- `packages/skills-catalog` is a workspace package.
- Build or validation regenerates `generated/catalog.json`.
- Validation covers frontmatter, id/key/slug uniqueness, directory shape, file
inventory, trust derivation, and stale generated output.
- Server and CLI can import the manifest without crawling arbitrary paths.
- Root `skills/` is not expanded with the app-shipped catalog.
Phase D, catalog APIs:
- Catalog list/detail/file APIs are read-only and covered by tests.
- Install-from-catalog creates auditable company-scoped skill records with
provenance metadata and materialized files.
- Company boundary and mutation permission checks match or exceed existing
company skill mutations.
- Duplicate and unsafe overwrite behavior is explicit and tested.
Phase E, catalog CLI:
- Operators can browse, search, inspect, and install app-shipped catalog skills.
- External source behavior remains routed through `skills import`.
- Output and errors follow the Phase B CLI conventions.
- Catalog install is clearly distinct from agent attach/sync in help and docs.
Phase F, update/reset/audit:
- Security review records decisions for origin hash, user modification detection,
reset, audit findings, and force behavior.
- Implementation follows the review or records explicit deferrals.
- Mutating reset/update actions are activity logged.
- Tests cover dangerous findings, force behavior, and unchanged/current states.
Phase G, adapter truth model:
- Adapter snapshots accurately report `unsupported`, `persistent`, or
`ephemeral`.
- Desired, missing, installed, stale, external, and required states are tested.
- External adapter plugins remain dynamically loaded. No hardcoded plugin imports
are added.
Phase H, UI:
- The existing Company Skills page is extended rather than replaced.
- UX guidance covers Company, Bundled, Optional, and External source views.
- Install preview shows source, trust, provenance, update state, and file
inventory.
- Agent attach/detach states are clear.
- Frontend handoff includes screenshots or equivalent browser evidence.
Phase I, initial skill content:
- Bundled and optional entries use the finalized frontmatter and category rules.
- Skill descriptions are specific enough for browse/search.
- No script-bearing skill lands without explicit security review evidence.
- Validation fixtures or tests cover representative content.
Phase J, QA and docs:
- QA validates CLI, catalog APIs, UI install, agent sync, portability, and adapter
snapshots against a dev instance.
- Blocking defects are linked as first-class issues.
- `doc/CLI.md`, `doc/DEVELOPING.md`, and skill workflow docs match shipped
behavior.
## Deferrals
- No cloud marketplace.
- No user-home tap registry.
- No hidden curator or autonomous catalog mutator.
- No normalized `agent_skills` table in the first release.
- No skill sets or bundles in the first release.
- No automatic install of every optional catalog skill.
- No replacement of company import/export as the portability path.