lshed 0.0.0 → 0.2.1
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 +40 -0
- package/LICENSE +21 -0
- package/README.md +185 -0
- package/dist/cli.js +1063 -0
- package/package.json +53 -7
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.1 — 2026-09-02
|
|
4
|
+
|
|
5
|
+
- `lshed list [--unused]` shows everything in the shed and which profiles use it.
|
|
6
|
+
- `lshed remove <key>` deletes a component or package from the shed. Refused while any profile still references it. Packages leave the manifest and lock only; the local clone stays.
|
|
7
|
+
- `lshed prune [--yes]` removes everything no profile uses. Lists without `--yes`.
|
|
8
|
+
- Manifest edits preserve your comments. Empty `packages:` is no longer written.
|
|
9
|
+
|
|
10
|
+
## 0.2.0 — 2026-09-02
|
|
11
|
+
|
|
12
|
+
A harness holds three kinds of things, and 0.1 treated them all the same. Running against a real `~/.claude` showed that 55 of 62 "skills" were files generated by one toolkit's installer, and the toolkit itself was a git clone.
|
|
13
|
+
|
|
14
|
+
- **Packages.** A `packages:` list in `lshed.yaml` records things you *installed* by source and version instead of copying them: `source: github:owner/repo@ref` (or `git:<url>#ref`), `into: <path under the agent root>`, optional `install: <command>`. `restore` clones a missing package at the commit pinned in `lshed.lock`; a package already present is never touched.
|
|
15
|
+
- **Generated files are skipped.** `init` recognises a directory as a package when it contains a `.git` with a remote, and recognises a stub as generated when one of its symlinks points inside a package. Both are left out of the shed and the managed set. Aliases that an installer creates without symlinks are not detected; use `init --exclude`.
|
|
16
|
+
- **`lshed.lock`** pins each package to a commit. Written by `init` from the existing clone, by `restore` on first clone, and by `update`.
|
|
17
|
+
- **`lshed update [ids...]`** fast-forwards packages and refreshes the lock.
|
|
18
|
+
- **Install commands run only with `--yes`.** Without it, `restore` and `update` print the commands and stop. A shed can be cloned from anywhere; running its shell commands should be a deliberate act.
|
|
19
|
+
- `git:` source scheme for non-GitHub remotes.
|
|
20
|
+
|
|
21
|
+
Packages are additive: switching to a profile that does not list one leaves it on disk. Removing a clone with a backup would mean copying a repository, which is the wrong tool for that job.
|
|
22
|
+
|
|
23
|
+
## 0.1.1 — 2026-09-02
|
|
24
|
+
|
|
25
|
+
Both fixes came from running 0.1.0 against a real 62-skill `~/.claude`.
|
|
26
|
+
|
|
27
|
+
- Skip regenerable directories when copying: `node_modules`, `.git`, `__pycache__`, `.venv`, cache dirs, `*.log`. A real harness went from 1.6 GB to 6.2 MB. Build output such as `dist/` is **not** ignored by default, because for some skills it is the deliverable.
|
|
28
|
+
- Extend the list with `ignore:` in `lshed.yaml`; the same list applies to `diff`, `save` and backups, so ignored files never show up as drift.
|
|
29
|
+
- Follow symlinks. A skill symlinked into `~/.claude/skills` used to be skipped silently; it is now captured and copied by content. Broken links are skipped.
|
|
30
|
+
- `init --exclude <id...>` leaves out components that do not belong in a shed, such as a toolkit with its own installer.
|
|
31
|
+
|
|
32
|
+
## 0.1.0 — 2026-09-02
|
|
33
|
+
|
|
34
|
+
First working release. Claude Code only.
|
|
35
|
+
|
|
36
|
+
- `init` scans `~/.claude` (skills, agents, commands, CLAUDE.md) into a shed and writes `lshed.yaml`
|
|
37
|
+
- `restore <profile>` with managed-set semantics, backups on by default, `--dry-run`
|
|
38
|
+
- `status`, `diff`, `save`
|
|
39
|
+
- Instructions are assembled as an `@`-import list, not merged
|
|
40
|
+
- `file:` sources only; `github:` is parsed but rejected until 0.2
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 leesongheon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# lshed
|
|
2
|
+
|
|
3
|
+
Keep your coding-agent harness — skills, subagents, commands, instructions — in a **shed**, and restore it on any machine with one command.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
lshed init --shed ~/lshed # scan ~/.claude into a shed + write lshed.yaml
|
|
7
|
+
lshed restore research # apply a profile anywhere
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The shed is a plain directory. Put it in a git repo, Dropbox, whatever. lshed does not sync it for you.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
Every new laptop, server, container or WSL box means setting up `~/.claude` again. dotfiles tools move **files**; they don't know what a skill, an agent or an instruction fragment is, they can't compose a subset per machine, and they can't tell which parts they placed and which were yours.
|
|
15
|
+
|
|
16
|
+
lshed adds three first-class ideas on top of "a directory in git":
|
|
17
|
+
|
|
18
|
+
| Idea | What it gives you |
|
|
19
|
+
|---|---|
|
|
20
|
+
| **Components** | every skill / agent / command / instruction fragment is one named part in the shed |
|
|
21
|
+
| **Profiles** | named recipes — `research`, `work`, `minimal` — that pick a subset of parts |
|
|
22
|
+
| **Managed set** | lshed remembers what it placed, so switching profiles removes only its own files and never touches yours |
|
|
23
|
+
|
|
24
|
+
Currently supports **Claude Code** (`~/.claude`). Other agents plug in through an adapter.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
npm install -g lshed
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Node 20 or newer.
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# 1. On the machine that already has your setup
|
|
38
|
+
lshed init --shed ~/lshed
|
|
39
|
+
cd ~/lshed && git init && git add -A && git commit -m "my harness" && git remote add origin <your private repo> && git push -u origin main
|
|
40
|
+
|
|
41
|
+
# 2. Edit ~/lshed/lshed.yaml — add profiles, drop parts you don't need everywhere
|
|
42
|
+
|
|
43
|
+
# 3. On any other machine
|
|
44
|
+
git clone <your private repo> ~/lshed
|
|
45
|
+
lshed restore research --shed ~/lshed # --shed only needed the first time
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## The manifest
|
|
49
|
+
|
|
50
|
+
`lshed.yaml` lives at the root of the shed. `init` generates it; edit it by hand from then on.
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
version: 1
|
|
54
|
+
agent: claude-code
|
|
55
|
+
|
|
56
|
+
components:
|
|
57
|
+
skills:
|
|
58
|
+
- id: paper-review # source defaults to file:./skills/paper-review
|
|
59
|
+
- id: grading-helper
|
|
60
|
+
agents:
|
|
61
|
+
- id: reviewer # file:./agents/reviewer.md
|
|
62
|
+
commands:
|
|
63
|
+
- id: summarize
|
|
64
|
+
instructions:
|
|
65
|
+
- id: base # file:./instructions/base.md
|
|
66
|
+
- id: research-style
|
|
67
|
+
|
|
68
|
+
profiles:
|
|
69
|
+
research:
|
|
70
|
+
skills: [paper-review]
|
|
71
|
+
agents: [reviewer]
|
|
72
|
+
instructions: [base, research-style] # order matters
|
|
73
|
+
teaching:
|
|
74
|
+
skills: [grading-helper]
|
|
75
|
+
commands: [summarize]
|
|
76
|
+
instructions: [base]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- 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`.
|
|
81
|
+
- `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
|
+
- 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
|
+
|
|
84
|
+
## Three kinds of things
|
|
85
|
+
|
|
86
|
+
A real `~/.claude` mixes three kinds of content, and they need different handling:
|
|
87
|
+
|
|
88
|
+
| Kind | Example | What lshed does |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| **Authored** | a skill you wrote, your `CLAUDE.md` | copies it into the shed |
|
|
91
|
+
| **Installed** | a toolkit you `git clone`d, a plugin | records source + commit; `restore` clones it back |
|
|
92
|
+
| **Generated** | stub skills an installer wrote for you | skips them; they return when the installer runs |
|
|
93
|
+
|
|
94
|
+
`init` sorts this out for you. A directory with a `.git` and a remote becomes a **package**. A skill whose symlink points inside a package is treated as generated and skipped. Everything else is authored and copied.
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
packages:
|
|
98
|
+
- id: gstack
|
|
99
|
+
source: github:garrytan/gstack@main # git:<url>#ref for other hosts
|
|
100
|
+
into: skills/gstack # where it lives under ~/.claude
|
|
101
|
+
install: ./setup # optional; run after clone, only with --yes
|
|
102
|
+
|
|
103
|
+
profiles:
|
|
104
|
+
default:
|
|
105
|
+
packages: [gstack]
|
|
106
|
+
skills: [add-drivers, domain-modeling]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`lshed.lock` pins each package to a commit, so a fresh machine gets the same version you had. `lshed update` moves it forward.
|
|
110
|
+
|
|
111
|
+
Rules that keep this safe:
|
|
112
|
+
|
|
113
|
+
- 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`.
|
|
115
|
+
- Packages are not part of the managed set. Switching profiles never deletes a clone.
|
|
116
|
+
- Installers sometimes create aliases without symlinks, which `init` cannot tell from authored skills. Leave those out with `--exclude`:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
lshed init --shed ~/harness --exclude _gstack-command connect-chrome
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Commands
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
lshed init [--shed <dir>] [--profile <name>] [--exclude <id...>]
|
|
126
|
+
lshed restore [profile] [--dry-run] [--no-backup] [--yes]
|
|
127
|
+
lshed update [ids...] [--dry-run] [--yes] pull packages forward, refresh lshed.lock
|
|
128
|
+
lshed status applied profile, managed paths, drift, packages
|
|
129
|
+
lshed diff files that differ between local and shed
|
|
130
|
+
lshed save [ids...] copy local edits back into the shed
|
|
131
|
+
lshed list [--unused] what is in the shed, and which profiles use it
|
|
132
|
+
lshed remove <key> drop a component or package from the shed
|
|
133
|
+
lshed prune [--yes] drop everything no profile uses
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`remove` and `prune` delete from the shed without a backup. The shed is meant to live in git; commit before you prune. Both refuse to touch anything a profile still lists, so the way to retire a part is to take it out of the profiles first.
|
|
137
|
+
|
|
138
|
+
Global options: `--shed <dir>` (or `LSHED_HOME`; after the first restore lshed remembers it), `--root <dir>` (agent config root, default `~/.claude`).
|
|
139
|
+
|
|
140
|
+
### What `restore` does
|
|
141
|
+
|
|
142
|
+
0. Clones any package in the profile that is missing, at the commit in `lshed.lock`.
|
|
143
|
+
1. Removes paths that the **previous** profile placed and the new one doesn't need.
|
|
144
|
+
2. Copies every part of the new profile into place.
|
|
145
|
+
3. Regenerates the instructions file.
|
|
146
|
+
|
|
147
|
+
Anything it overwrites or removes is backed up first under `~/.claude/lshed/backups/<timestamp>/`, unless you pass `--no-backup`. Files lshed never placed are left alone. `--dry-run` prints the plan and writes nothing.
|
|
148
|
+
|
|
149
|
+
### Ownership
|
|
150
|
+
|
|
151
|
+
The shed is the source of truth for authored parts: `save` copies local edits back for `file:` components. Packages are owned by their upstream: `update` pulls them, `save` ignores them.
|
|
152
|
+
|
|
153
|
+
## Where things live
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
<shed>/
|
|
157
|
+
lshed.yaml
|
|
158
|
+
skills/<id>/ agents/<id>.md commands/<id>.md instructions/<id>.md
|
|
159
|
+
|
|
160
|
+
~/.claude/
|
|
161
|
+
skills/ agents/ commands/ CLAUDE.md ← placed by restore
|
|
162
|
+
lshed/state.json ← which profile, which paths are managed
|
|
163
|
+
lshed/instructions/<id>.md ← fragments imported by CLAUDE.md
|
|
164
|
+
lshed/backups/<timestamp>/ ← whatever restore replaced
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`state.json` is per machine and is not part of the shed.
|
|
168
|
+
|
|
169
|
+
## Not in scope (yet)
|
|
170
|
+
|
|
171
|
+
- MCP servers and secrets. Planned: the manifest names the keys, values are injected locally, nothing secret enters the shed.
|
|
172
|
+
- `settings.json` merging (hooks, permissions).
|
|
173
|
+
- `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
|
+
- Windows and macOS have not been tested. The code avoids platform-specific paths, but treat 0.1 as Linux/WSL.
|
|
176
|
+
|
|
177
|
+
## Troubleshooting
|
|
178
|
+
|
|
179
|
+
- **"창고 위치를 모릅니다"** — pass `--shed <dir>` or set `LSHED_HOME`. After one successful `restore`, lshed remembers it.
|
|
180
|
+
- **restore replaced my `CLAUDE.md`** — it is in `~/.claude/lshed/backups/<timestamp>/CLAUDE.md`. Move its content into a fragment in the shed and add that fragment to your profile.
|
|
181
|
+
- **I edited a skill locally and want to keep it** — `lshed diff` to see, `lshed save <id>` to push it into the shed, then commit the shed.
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT
|