lshed 0.17.0 → 0.17.2
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 +13 -0
- package/README.ko.md +12 -1
- package/README.md +12 -1
- package/dist/cli.js +193 -192
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.2 — 2026-09-09
|
|
4
|
+
|
|
5
|
+
- Fix: `restore --yes` stopped at the first `install:` command that failed, before any part was placed and before the profile was recorded. The fifth Windows pass hit it: gstack's `./setup` is an sh script, cmd.exe answered `'.' is not recognized`, and the run ended with exit 1, nine parts unplaced and no state written. A failing command now prints `! install failed: …` under the package, the restore goes on to place everything and record the profile, the failed commands are listed at the end (`N install commands failed. Everything else was placed.`), and `restore` and `update` exit 1 so a script still notices.
|
|
6
|
+
- Fix: on Windows, Node 24 printed a `DEP0190` deprecation warning on every `claude plugin install` and every `lshed check`, because the `.cmd` wrapper was spawned through the shell with an argument array. lshed now builds the one-line command itself, quoting arguments the way CreateProcess reads them, and spawns that.
|
|
7
|
+
- The `--yes` help said install commands are "shown, not run" without it, while plugin installs always run through Claude Code's own package manager; the text now says which commands the flag gates. The README's troubleshooting section says what to do when `restore --yes` reports a failed install on Windows (run it from Git Bash, and check the package's own requirements: gstack needs bun).
|
|
8
|
+
|
|
9
|
+
## 0.17.1 — 2026-09-09
|
|
10
|
+
|
|
11
|
+
- Security: a shed you `restore` could reach outside the agent's own root. A component `id` or a package `into` of `../../.bashrc` passed the manifest check, because the id pattern allowed a `..` path segment and `into` only forbade a leading slash, so `restore` would overwrite files anywhere under the home directory (a login file, an SSH `authorized_keys`, an autostart entry). The manifest now rejects `.` and `..` path segments in ids and in `into`, and the git installer refuses a package path that resolves outside the agent root. Everything a shed places stays under the root it targets.
|
|
12
|
+
- Security: a `git:` package source was passed to `git ls-remote`/`git clone` as the first positional word, so a URL beginning with `-` became a git option — `git:--upload-pack=<command>#main` ran that command during `restore` or `update --dry-run`. lshed now rejects a `git:` URL that starts with `-`, and passes the URL after a `--` separator so git can never read it as an option.
|
|
13
|
+
- Security: rewriting an entry file (`~/.claude.json`, a Codex `config.toml`) replaced it through a fresh temp file, which took the process umask and widened the mode — a `~/.claude.json` kept at `600` for its OAuth token came back at `664`, readable by the group. The write now preserves the existing file's mode, and creates a new one owner-only (`600`).
|
|
14
|
+
- The README gains a **Trust** section: a shed is executable, not just data — `restore` places configs, fills `${VAR}` from your shell, and with `--yes` runs each package's `install:` — so restore only a shed you trust as much as your own dotfiles, and keep yours private.
|
|
15
|
+
|
|
3
16
|
## 0.17.0 — 2026-09-09
|
|
4
17
|
|
|
5
18
|
- A Codex, Gemini, Copilot, Cursor or Antigravity user who followed the quick start without `--agent` got two quiet failures: `init` scanned a `~/.claude` that did not exist and wrote a shed with zero parts, and after `restore --agent codex` on another machine, `status`, `diff` and `save` without flags looked at `~/.claude` again and reported nothing applied. Now `init` refuses to scan a root that is not there and names the agent folders it did find (`Found here: codex (~/.codex). Try: lshed init --agent codex`), and when neither `--agent`, `LSHED_AGENT` nor a shed says which agent, lshed uses the one agent on the machine that has lshed state; Claude Code keeps precedence when it has state, and with several candidates the command asks you to say which. The README's quick start and loop are written for any agent, and the smoke suite walks them on a home folder that has only `~/.codex`.
|
package/README.ko.md
CHANGED
|
@@ -174,7 +174,7 @@ Some entries need environment variables that are not set. Secrets never go in th
|
|
|
174
174
|
mcp:notion: NOTION_AUTHORIZATION
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
그 뒤 손이 가는 것은 둘입니다. 패키지의 `install:`은 clone해 온 저장소의 셸 명령이므로 `restore`는 보여 주고 멈춥니다. 직접 돌리거나 `--yes`로 다시 실행하세요. MCP 서버는 시크릿을 `${VAR}`로 참조하니 셸에서 export하면 Claude Code가 채웁니다. 이후로는 인자 없는 `lshed restore`가 마지막 프로필을 다시 적용하고, 창고 위치도 기억합니다.
|
|
177
|
+
그 뒤 손이 가는 것은 둘입니다. 패키지의 `install:`은 clone해 온 저장소의 셸 명령이므로 `restore`는 보여 주고 멈춥니다. 직접 돌리거나 `--yes`로 다시 실행하세요. `--yes`로 돌린 명령이 실패해도 복원은 멈추지 않습니다. 부품은 그대로 놓이고, 실패는 끝에 모아 보여 주며, `restore`는 exit 1로 끝납니다. MCP 서버는 시크릿을 `${VAR}`로 참조하니 셸에서 export하면 Claude Code가 채웁니다. 이후로는 인자 없는 `lshed restore`가 마지막 프로필을 다시 적용하고, 창고 위치도 기억합니다.
|
|
178
178
|
|
|
179
179
|
### 프로필 이름 대신 골라서 넣기
|
|
180
180
|
|
|
@@ -260,6 +260,12 @@ profiles:
|
|
|
260
260
|
|
|
261
261
|
상속은 더하기만 합니다. 부모의 부품이 먼저, 자기 것이 뒤에 오고, 지침도 그 순서로 `CLAUDE.md`에 들어갑니다. 부모보다 *적게* 가지려면 상속하지 말고 원하는 것을 직접 적으세요. 없는 부모나 순환은 아무것도 건드리기 전에 `lshed.yaml` 오류로 알리고, `lshed list`는 상속받는 프로필도 그 부품을 쓰는 것으로 셉니다.
|
|
262
262
|
|
|
263
|
+
헷갈리기 쉬운 세 가지가 있는데, 일부러 나눠 두었습니다.
|
|
264
|
+
|
|
265
|
+
- **안 쓰되 남겨 두기** — 그 id 를 프로필에서 빼기만 합니다. `restore` 가 이 기계에서 치우고(백업) 부품은 창고에 남아, 다른 프로필은 계속 쓰고, id 를 다시 넣어 restore 하면 돌아옵니다. 되돌릴 수 있는 일상적 "제거"이고, 이걸 위해 창고에서 무엇을 뺄 필요는 없습니다.
|
|
266
|
+
- **상속받은 목록을 줄이기** — `extends` 는 더하기만 하므로, 프로필을 상속하면서 그중 한 부품만 빼는 것은 안 됩니다. 작은 목록을 그냥 나열하거나, 공용 base 를 갈라 그것을 상속하세요. "빼기가 없다"는 말은 오직 이 뜻입니다.
|
|
267
|
+
- **창고에서 부품을 삭제하기** — `lshed remove <id>` (어떤 프로필이 아직 그 부품을 나열하면 거부하니 먼저 프로필에서 빼세요) 또는 아무 프로필도 안 쓰는 것을 치우는 `lshed prune`. 영구적이며, 창고에서 파일을 실제로 없애는 유일한 경우입니다.
|
|
268
|
+
|
|
263
269
|
### 다른 에이전트도 같은 창고로
|
|
264
270
|
|
|
265
271
|
Codex, Gemini CLI, Copilot CLI, Cursor, Google Antigravity(`agy`)는 모두 `<자기 설정 디렉터리>/skills/<이름>/SKILL.md`를 읽습니다. Claude Code와 같은 배치이고, Antigravity를 뺀 나머지는 공용 `~/.agents/skills/`도 읽습니다. 그래서 창고 하나로 전부를 채울 수 있습니다. 대상은 `--agent`로 고릅니다. Codex의 스킬은 Codex 문서가 정한 위치인 `~/.agents/skills`에 놓으므로, 한 기기에서 스킬은 `--agent codex`와 `--agent agents` 중 하나로만 넣으세요.
|
|
@@ -509,6 +515,10 @@ lshed report [--open | --url] 이슈에 붙여 넣을 이 설
|
|
|
509
515
|
|
|
510
516
|
직접 만든 부품의 진실은 창고입니다. `save`는 `file:` 부품의 로컬 편집을 창고로 되가져오고, 링크된 부품은 곧 창고입니다. 패키지의 주인은 upstream입니다. `update`가 당겨 오고, `save`는 건드리지 않습니다.
|
|
511
517
|
|
|
518
|
+
### 신뢰
|
|
519
|
+
|
|
520
|
+
창고는 그냥 데이터가 아니라 실행됩니다. `restore`는 에이전트 설정에 파일을 놓고, 자신이 쓰는 설정의 `${VAR}`를 당신 셸의 값으로 채우며, `--yes`면 각 패키지의 `install:` 셸 명령을 실행합니다. 그러니 자신의 dotfiles만큼 신뢰하는 창고만 `restore`하고, 당신 것은 비공개로(비공개 git 저장소) 두세요. 남의 창고를 `restore`하는 것은 그 사람에게 당신의 기계를 내주는 것에 가깝습니다 — 매니페스트에 `install:` 명령이 있을 수 있고, 그것이 쓰는 설정이 MCP 서버를 어떤 URL로 향하게 해 당신의 비밀 하나를 그리로 보낼 수 있습니다. lshed는 자신이 놓는 것을 에이전트 루트 안으로 가두고 git 옵션을 몰래 넣을 수 있는 패키지 출처를 거부하지만, 창고의 `install:`이 무엇을 돌리는지, 설정이 비밀을 어디로 보내는지까지 보증하지는 못합니다. 당신이 쓰지 않은 창고는 곧 돌리려는 남의 코드처럼 다루세요.
|
|
521
|
+
|
|
512
522
|
## 어디에 무엇이 있나
|
|
513
523
|
|
|
514
524
|
```
|
|
@@ -560,6 +570,7 @@ lshed report [--open | --url] 이슈에 붙여 넣을 이 설
|
|
|
560
570
|
- **`status`가 같은 새 항목을 계속 보여 준다** — 설치기 별칭이거나 임시 파일입니다. `lshed.yaml`의 `exclude:`에 넣으세요.
|
|
561
571
|
- **restore가 MCP 변수가 없다고 한다** — 셸 프로필에서 export하고 Claude Code를 다시 시작하세요. `~/.claude.json`의 자리표시자는 맞게 들어간 것이고, Claude Code가 시작할 때 채웁니다.
|
|
562
572
|
- **restore가 훅 경로를 엉뚱하게 썼다** — 창고는 홈 경로를 `${HOME}/…`로 담습니다. 이 기기에서 다른 곳을 가리켜야 하면 창고의 JSON을 `${HOME}`이나 다른 변수로 고치고 다시 `restore`하세요. 홈 밖의 경로(`D:\tools\x.exe`, `/opt/x`)는 쓴 그대로 옮겨지며, 휴대성은 사용자 몫입니다.
|
|
573
|
+
- **Windows에서 `restore --yes`가 install 명령이 실패했다고 한다** — Windows에서 `install:`은 cmd.exe로 돌기 때문에 sh용으로 쓴 `./setup`은 시작조차 못 합니다(`'.' is not recognized`). 나머지는 다 놓였으니 Git Bash에서 그 명령을 직접 돌리고(`cd ~/.claude/skills/<패키지> && ./setup`), 패키지 자체의 요구 사항도 확인하세요(gstack은 bun이 필요합니다).
|
|
563
574
|
- **Windows에서 `--link`가 파일을 복사했다** — 파일 하나짜리 링크는 개발자 모드가 필요합니다. 켜고 다시 `restore`하거나, 복사본을 그대로 두세요. 이후 `restore`는 그것을 `= … (copy; …)`로 알리고 건드리지 않으며, 편집은 `save`로 되가져옵니다.
|
|
564
575
|
- **sync가 충돌로 멈췄다** — `cd <창고> && git pull --rebase`, 해결, `git rebase --continue`, 그리고 다시 `lshed sync`.
|
|
565
576
|
|
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ Some entries need environment variables that are not set. Secrets never go in th
|
|
|
174
174
|
mcp:notion: NOTION_AUTHORIZATION
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
Two things need you afterwards. Package `install:` commands are shell commands from a repository you cloned, so `restore` shows them and stops; run them yourself or rerun with `--yes`. MCP servers reference secrets as `${VAR}`; export the variables in your shell and Claude Code fills them in. From then on `lshed restore` with no arguments reapplies the last profile, and the shed location is remembered.
|
|
177
|
+
Two things need you afterwards. Package `install:` commands are shell commands from a repository you cloned, so `restore` shows them and stops; run them yourself or rerun with `--yes`. A command that fails under `--yes` does not stop the restore: the parts are still placed, the failure is listed at the end, and `restore` exits 1. MCP servers reference secrets as `${VAR}`; export the variables in your shell and Claude Code fills them in. From then on `lshed restore` with no arguments reapplies the last profile, and the shed location is remembered.
|
|
178
178
|
|
|
179
179
|
### Picking instead of naming a profile
|
|
180
180
|
|
|
@@ -260,6 +260,12 @@ profiles:
|
|
|
260
260
|
|
|
261
261
|
Inheritance only adds. The parent's parts come first, then the profile's own, and instructions keep that order in the generated `CLAUDE.md`. To get *less* than the parent, do not extend it; list what you want. A missing parent or a cycle is reported as a `lshed.yaml` error before anything is touched, and `lshed list` counts a part as used by every profile that inherits it.
|
|
262
262
|
|
|
263
|
+
Three things are easy to confuse. They are separate on purpose:
|
|
264
|
+
|
|
265
|
+
- **Stop using a part, keep it** — leave its id out of the profile. `restore` takes it off this machine (backed up); the part stays in the shed, another profile still uses it, and putting the id back and restoring brings it back. This is the reversible, everyday "remove", and nothing has to leave the shed for it.
|
|
266
|
+
- **A smaller inherited set** — `extends` only adds, so you cannot inherit a profile and drop one part from it. List the smaller set outright, or split a shared base out and extend that. This is the only sense in which there is "no subtraction".
|
|
267
|
+
- **Delete a part from the shed** — `lshed remove <id>` (it refuses while any profile still lists the part, so take it out of profiles first) or `lshed prune` for whatever no profile uses. This is the permanent one, and the only one that removes the file from the shed.
|
|
268
|
+
|
|
263
269
|
### Other agents, same shed
|
|
264
270
|
|
|
265
271
|
Codex, Gemini CLI, Copilot CLI, Cursor and Google Antigravity (`agy`) all read skills from `<their config dir>/skills/<name>/SKILL.md`, the same layout Claude Code uses, and all but Antigravity also read the shared `~/.agents/skills/`. So one shed can serve them all. Pick the target with `--agent`. For Codex, skills go to `~/.agents/skills` because that is the location Codex documents, so use either `--agent codex` or `--agent agents` for skills on one machine, not both:
|
|
@@ -509,6 +515,10 @@ On a conflict it aborts the rebase, leaves the shed clean with your commit intac
|
|
|
509
515
|
|
|
510
516
|
The shed is the source of truth for authored parts: `save` copies local edits back for `file:` components, and a linked part is the shed. Packages are owned by their upstream: `update` pulls them, `save` ignores them.
|
|
511
517
|
|
|
518
|
+
### Trust
|
|
519
|
+
|
|
520
|
+
A shed is executable, not just data. `restore` places files into your agent's config, fills `${VAR}` from your shell into the configs it writes, and with `--yes` runs each package's `install:` shell command. So restore only a shed you trust as much as your own dotfiles, and keep yours private (a private git repo). Restoring someone else's shed is close to handing them your machine: its manifest can carry an `install:` command, and a config it writes can point an MCP server at a URL that then receives one of your secrets. lshed confines what it places to the agent's own root and rejects a package source that could smuggle a git option, but it cannot vouch for what a shed's `install:` runs or where a config sends a secret. Treat a shed you did not write like any other code you are about to run.
|
|
521
|
+
|
|
512
522
|
## Where things live
|
|
513
523
|
|
|
514
524
|
```
|
|
@@ -560,6 +570,7 @@ The shed is the source of truth for authored parts: `save` copies local edits ba
|
|
|
560
570
|
- **`status` keeps listing the same new things** — they are installer aliases or scratch. Add them to `exclude:` in `lshed.yaml`.
|
|
561
571
|
- **restore says an MCP variable is missing** — export it in your shell profile and restart Claude Code. The placeholder in `~/.claude.json` is correct; Claude Code fills it at startup.
|
|
562
572
|
- **restore wrote a hook with the wrong path** — the shed stores home paths as `${HOME}/…`. If a command points elsewhere on this machine, edit the JSON in the shed to use `${HOME}` or another variable and `restore` again. Paths outside your home directory (`D:\tools\x.exe`, `/opt/x`) are copied as written and are your job to keep portable.
|
|
573
|
+
- **`restore --yes` says an install command failed on Windows** — `install:` runs in cmd.exe there, so a `./setup` written for sh cannot start (`'.' is not recognized`). Everything else is placed; run the command from Git Bash (`cd ~/.claude/skills/<package> && ./setup`) and check the package's own requirements (gstack needs bun).
|
|
563
574
|
- **`--link` copied a file on Windows** — single-file links need Developer Mode. Turn it on and `restore` again, or keep the copy: later restores report it as `= … (copy; …)` and leave it alone, and `save` brings edits back from it.
|
|
564
575
|
- **sync stopped on a conflict** — `cd <shed> && git pull --rebase`, resolve, `git rebase --continue`, then `lshed sync` again.
|
|
565
576
|
|