lshed 0.2.1 → 0.4.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.
- package/CHANGELOG.md +22 -0
- package/README.md +37 -5
- package/dist/cli.js +793 -314
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 — 2026-09-03
|
|
4
|
+
|
|
5
|
+
Hand-configured MCP servers travel with the profile. Secret values do not.
|
|
6
|
+
|
|
7
|
+
- New category `mcp` for Claude Code: each user-scope server in `~/.claude.json` becomes `mcp/<name>.json` in the shed. `restore` writes only `mcpServers.<name>` and leaves the rest of the file (machine ID, session state, servers you added by hand) untouched. The file is replaced atomically.
|
|
8
|
+
- `init` masks values under `env` and `headers` whose key looks like a secret with `${VAR}` (`EXA_API_KEY` → `${EXA_API_KEY}`, `Authorization: Bearer …` → `Bearer ${NOTION_AUTHORIZATION}`). Claude Code expands `${VAR}` from the environment in every scope, so `restore` places the placeholder verbatim and no secret passes through lshed. Verified against the real binary.
|
|
9
|
+
- `restore` and `status` list the variables a profile needs that are not set in the current shell.
|
|
10
|
+
- `diff` shows entry changes by key path; `save` keeps placeholders that still match and masks newly added secret-looking keys.
|
|
11
|
+
- Profile switches remove only the servers lshed placed, backing each up as JSON.
|
|
12
|
+
- Adapter interface: `entries()` returns categories that live as JSON entries instead of files, with `secretKeys` and `expandsEnv` policy. The default root honours `CLAUDE_CONFIG_DIR`.
|
|
13
|
+
- Managed-set paths for entries are written as `mcp:<name>`; a colon cannot appear in a path segment, so they never collide with files.
|
|
14
|
+
|
|
15
|
+
## 0.3.0 — 2026-09-02
|
|
16
|
+
|
|
17
|
+
Claude Code plugins are packages now. On the machine this was built against, the five installed plugins were the only thing a fresh `restore` still left out, and two of them carry MCP servers.
|
|
18
|
+
|
|
19
|
+
- `init` records each user-scope plugin as `claude-plugin:<name>@<marketplace>` and each GitHub-backed marketplace as `claude-marketplace:<owner/repo>`. Project-scope plugins belong to their project and are skipped.
|
|
20
|
+
- `restore` adds missing marketplaces first, then installs missing plugins through `claude plugin install`. Both are the agent's own package manager, so they run without `--yes`; `--yes` is forwarded as `-y` to accept a marketplace-declared install command.
|
|
21
|
+
- Plugins cannot be pinned. `lshed.lock` records the version that actually got installed and `status` shows when it differs from what another machine had. `update` runs `claude plugin update`.
|
|
22
|
+
- Installers are an interface now. `github:`/`git:` live in core; an adapter contributes its own (`ClaudeCodeAdapter` provides the two above). Install order follows installer priority, then manifest order.
|
|
23
|
+
- Lock entries use `rev` instead of `commit`. Old locks still read.
|
|
24
|
+
|
|
3
25
|
## 0.2.1 — 2026-09-02
|
|
4
26
|
|
|
5
27
|
- `lshed list [--unused]` shows everything in the shed and which profiles use it.
|
package/README.md
CHANGED
|
@@ -64,12 +64,15 @@ components:
|
|
|
64
64
|
instructions:
|
|
65
65
|
- id: base # file:./instructions/base.md
|
|
66
66
|
- id: research-style
|
|
67
|
+
mcp:
|
|
68
|
+
- id: exa # file:./mcp/exa.json — secrets replaced by ${VAR}
|
|
67
69
|
|
|
68
70
|
profiles:
|
|
69
71
|
research:
|
|
70
72
|
skills: [paper-review]
|
|
71
73
|
agents: [reviewer]
|
|
72
74
|
instructions: [base, research-style] # order matters
|
|
75
|
+
mcp: [exa]
|
|
73
76
|
teaching:
|
|
74
77
|
skills: [grading-helper]
|
|
75
78
|
commands: [summarize]
|
|
@@ -77,10 +80,25 @@ profiles:
|
|
|
77
80
|
```
|
|
78
81
|
|
|
79
82
|
- Component `source` accepts `file:<path relative to the shed>`. Package `source` accepts `github:owner/repo@ref` or `git:<url>#ref`.
|
|
80
|
-
- Category names come from the adapter. For Claude Code: `skills`, `agents`, `commands`, `instructions`.
|
|
83
|
+
- Category names come from the adapter. For Claude Code: `skills`, `agents`, `commands`, `instructions`, `mcp`.
|
|
81
84
|
- `ignore:` at the top level adds to the built-in list of things never copied: `node_modules`, `.git`, `__pycache__`, `.venv`, cache directories, `*.log`. Build output like `dist/` is not ignored by default, since some skills ship it. Add it yourself if your parts rebuild from source.
|
|
82
85
|
- Instructions are not merged. `restore` writes a `CLAUDE.md` that `@`-imports each fragment in order, so a fragment edit shows up without re-running anything. Your original `CLAUDE.md` is backed up the first time.
|
|
83
86
|
|
|
87
|
+
## MCP servers and secrets
|
|
88
|
+
|
|
89
|
+
User-scope MCP servers live in `~/.claude.json`, next to machine IDs and session state. lshed treats each server as a component of category `mcp`: the shed holds `mcp/<name>.json`, and `restore` edits only the `mcpServers.<name>` key of `~/.claude.json`, leaving everything else in that file alone.
|
|
90
|
+
|
|
91
|
+
**No secret value enters the shed.** `init` replaces values under `env` and `headers` whose key looks like a secret (`key`, `token`, `secret`, `pass`, `auth`, `credential`, `cookie`, `session`) with a `${VAR}` placeholder:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{ "type": "stdio", "command": "npx", "args": ["-y", "exa-mcp-server"],
|
|
95
|
+
"env": { "EXA_API_KEY": "${EXA_API_KEY}" } }
|
|
96
|
+
{ "type": "http", "url": "https://mcp.notion.com/mcp",
|
|
97
|
+
"headers": { "Authorization": "Bearer ${NOTION_AUTHORIZATION}" } }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`restore` writes the placeholder as is. Claude Code expands `${VAR}` from the environment when it starts the server, so the value only ever lives in your shell (`export EXA_API_KEY=...` in `~/.zshrc`, or however you manage secrets). `restore` and `status` list the variables the profile needs that are not set. The heuristic is a suggestion: edit the JSON in the shed to add or remove placeholders, and `init` warns when something in `args` or `url` looks like a token. `save` keeps existing placeholders and masks new secret-looking keys, so a rotated key never leaks into the shed by accident.
|
|
101
|
+
|
|
84
102
|
## Three kinds of things
|
|
85
103
|
|
|
86
104
|
A real `~/.claude` mixes three kinds of content, and they need different handling:
|
|
@@ -108,10 +126,22 @@ profiles:
|
|
|
108
126
|
|
|
109
127
|
`lshed.lock` pins each package to a commit, so a fresh machine gets the same version you had. `lshed update` moves it forward.
|
|
110
128
|
|
|
129
|
+
Claude Code plugins are packages too, with their own scheme. `init` finds them in `~/.claude/plugins`:
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
packages:
|
|
133
|
+
- id: claude-plugins-official
|
|
134
|
+
source: claude-marketplace:anthropics/claude-plugins-official
|
|
135
|
+
- id: exa
|
|
136
|
+
source: claude-plugin:exa@claude-plugins-official
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`restore` adds the marketplace, then runs `claude plugin install exa@claude-plugins-official`. Plugins cannot be pinned to a version, so the lock records whatever got installed and `status` tells you when it differs from the machine you came from. Plugins that bundle MCP servers bring them along.
|
|
140
|
+
|
|
111
141
|
Rules that keep this safe:
|
|
112
142
|
|
|
113
143
|
- A package that is already present is never touched by `restore`. Your local checkout is yours.
|
|
114
|
-
- `install:` is a shell command. `restore` and `update` **print it and stop** unless you pass `--yes`.
|
|
144
|
+
- `install:` is a shell command. `restore` and `update` **print it and stop** unless you pass `--yes`. Plugin installs go through Claude Code's own package manager and run without it; `--yes` is forwarded as `-y` for plugins that declare an install command.
|
|
115
145
|
- Packages are not part of the managed set. Switching profiles never deletes a clone.
|
|
116
146
|
- Installers sometimes create aliases without symlinks, which `init` cannot tell from authored skills. Leave those out with `--exclude`:
|
|
117
147
|
|
|
@@ -156,22 +186,24 @@ The shed is the source of truth for authored parts: `save` copies local edits ba
|
|
|
156
186
|
<shed>/
|
|
157
187
|
lshed.yaml
|
|
158
188
|
skills/<id>/ agents/<id>.md commands/<id>.md instructions/<id>.md
|
|
189
|
+
mcp/<id>.json ← secrets as ${VAR}
|
|
159
190
|
|
|
160
191
|
~/.claude/
|
|
161
192
|
skills/ agents/ commands/ CLAUDE.md ← placed by restore
|
|
193
|
+
~/.claude.json mcpServers.<id> ← one key per mcp component; the rest of the file is untouched
|
|
162
194
|
lshed/state.json ← which profile, which paths are managed
|
|
163
195
|
lshed/instructions/<id>.md ← fragments imported by CLAUDE.md
|
|
164
196
|
lshed/backups/<timestamp>/ ← whatever restore replaced
|
|
165
197
|
```
|
|
166
198
|
|
|
167
|
-
`state.json` is per machine and is not part of the shed.
|
|
199
|
+
`state.json` is per machine and is not part of the shed. If `CLAUDE_CONFIG_DIR` is set, lshed uses it as the root and expects `.claude.json` inside it, as Claude Code does.
|
|
168
200
|
|
|
169
201
|
## Not in scope (yet)
|
|
170
202
|
|
|
171
|
-
-
|
|
203
|
+
- Secrets beyond "name the variable". Encrypted values, `op://` references and OS keychains are possible later; today lshed is deliberately no better than dotfiles here.
|
|
204
|
+
- Project-scope MCP servers (`.mcp.json`, `~/.claude.json` `projects.*`). They belong to the project.
|
|
172
205
|
- `settings.json` merging (hooks, permissions).
|
|
173
206
|
- `sync` (a git pull/push wrapper). Use git in the shed directly for now.
|
|
174
|
-
- Plugins installed through Claude Code's marketplace. They are packages too; recording them is next.
|
|
175
207
|
- Windows and macOS have not been tested. The code avoids platform-specific paths, but treat 0.1 as Linux/WSL.
|
|
176
208
|
|
|
177
209
|
## Troubleshooting
|