1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
> [!NOTE]
> This repository is archived. pinch hasn't been worked on since
> March 2026 and is no longer maintained.

# pinch

A plugin manager for Claude Code (and [pi](https://github.com/nichochar/pi-coding-agent),
which reads from the same `~/.claude/skills/` directory). Installs skills and
commands as symlinks. Rust, single binary, keep it simple.

Only skills and commands are supported for now. Pinch will fail loudly if asked
to manage anything else.

## Concepts

A **skill** is a directory containing a `SKILL.md` file (and optionally other
files). Claude and pi discover skills by scanning `~/.claude/skills/<name>/`.

A **command** is a slash command — an `.md` file (or directory) that Claude Code
discovers by scanning `~/.claude/commands/`.

A **marketplace** is a git repo that contains one or more plugins, described by
a `.claude-plugin/marketplace.json` manifest.

A **package** is a git repo that contains one or more skills and/or commands. The repo is cloned
(bare) into pinch's cache; worktrees are created per revision so multiple
versions can coexist. Individual skills are symlinked into `~/.claude/skills/`.

## Directory layout

```
~/.config/pinch/           # XDG config (conf.d style)
  conf.d/
    base.yml              # any number of .yml files, merged alphabetically
    work.yml

~/.cache/pinch/            # managed by pinch
  repos/
    github.com/
      user/repo.git/                   # bare clone
        pinch-worktrees/
          abc123def456/                # worktree at rev abc123def456
          def789012345/                # worktree at rev def789012345

~/.local/share/pinch/
  pinch-lock.yml          # lockfile

~/.claude/skills/
  jj -> ~/.cache/pinch/repos/github.com/user/repo.git/pinch-worktrees/abc123def456/plugins/jj/skills/jj
  gh-pr -> ~/.cache/pinch/repos/github.com/user/repo.git/pinch-worktrees/def789012345/plugins/gh-pr/skills/gh-pr

~/.claude/commands/
  deploy.md -> ~/.cache/pinch/repos/github.com/user/repo.git/pinch-worktrees/abc123def456/plugins/ops/commands/deploy.md
```

## Config (manifest)

Config files live in `~/.config/pinch/conf.d/`. All `.yml` files are merged
alphabetically. Duplicate plugin names across files are a hard error.
Marketplace definitions are scoped per-file — the same marketplace name can
appear in different files at different versions.

```yml
marketplaces:
  my-skills:
    src: https://github.com/user/skills-repo
    version: main                 # branch/tag to track
    manifest: custom/path/marketplace.json  # optional, defaults to .claude-plugin/marketplace.json
    plugins:
      - jj
      - gh-pr
```

Plugins are listed under their marketplace. Each plugin inherits the
marketplace's `src`, `version`, and `manifest`. The plugin path within the repo
is discovered from `.claude-plugin/marketplace.json` (or the marketplace's
`manifest` override).

Removing a plugin is done by removing it from the config and re-running
`pinch install`.

## Lockfile

Lives at `~/.local/share/pinch/pinch-lock.yml`. Records the resolved commit
SHA for each skill. `install` reads from the lockfile; `update` writes to it.

```yml
plugins:
  jj:
    src: https://github.com/user/skills-repo
    path: skills/jj
    rev: abc123def456...     # resolved commit SHA

  gh-pr:
    src: https://github.com/user/skills-repo
    path: skills/gh-pr
    rev: abc123def456...
```

## Commands

### `pinch install`

Reads the **lockfile** (not the config). For each entry:

1. Ensures the repo is cloned/fetched and checked out at the locked `rev`.
2. Creates symlinks from `~/.claude/skills/<name>` and `~/.claude/commands/<name>`
   → the corresponding paths in the cached repo.
3. Removes any symlinks in `~/.claude/skills/` and `~/.claude/commands/` that
   point into `~/.cache/pinch/` but are no longer in the lockfile.

Idempotent. Safe to run repeatedly.

Fails loudly if there is no lockfile (tells you to run `pinch update` first).

Optional: `pinch install --update` — runs update then install in one shot.

### `pinch update`

Reads the **config** (merged conf.d). For each skill:

1. Fetches the repo.
2. Resolves the `version` (branch/tag) to a commit SHA.
3. Writes the lockfile.

Does **not** touch symlinks or `~/.claude/skills/`.

Optional: `pinch update --install` — runs update then install in one shot.

Optional: `pinch update <name>` — updates only the named skill(s).

## Development

This project was built entirely through AI-assisted development — pair
programming with Claude Code (and pi). The humans provided direction, design
decisions, and review; the AI wrote the code, tests, and docs.

## Non-goals (for now)

- Dependencies between packages
- Other plugin types (extensions, themes, custom tools)
- A registry / central index
- Running in project-scoped mode (everything is user-global)