lshed 0.17.4 → 0.17.5

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.5 — 2026-09-18
4
+
5
+ - README reorganized for the reader who wants to use lshed, not for the record of how it was built: install and quick start first, a shorter "Why not just git?", the walkthrough, then a reference section (commands, manifest, kinds, MCP, settings, what `restore` and `sync` do, where things live), trust, troubleshooting. The per-pass verification history, tool versions and what each pass found moved to `docs/VERIFICATION.md`, with a five-row summary table left in the README; adapter internals (which MCP field each tool gets, which Claude Code version a behaviour was checked against) were cut from the README. The Korean README follows the same structure.
6
+ - `lshed check --agent cursor` (and the probe) now pass `--trust` to Cursor's `agent -p`. Without it Cursor prints a "Workspace Trust Required" notice for the temporary folder the question is asked from and gives no answer, so the check reported the skill as not read when the tool had simply not looked. Found on the first Cursor run with a key (agent 2026.09.15): every placement check had passed and every answer was empty.
7
+ - `restore --fresh-only` and `restore --agent installed`, for a bootstrap that runs on every start of a container or shell. `--fresh-only` leaves a root that already has lshed state alone — one line saying which profile is applied there and when, exit 0 — so the entrypoint no longer has to know where each agent keeps its `state.json`. `--agent installed` restores into every agent whose CLI is on `PATH` (claude, codex, gemini, copilot, agent for Cursor, agy), one after another and each with its own state, and exits 1 at the end if any of them failed; it needs `--shed` or `LSHED_HOME`, since a fresh machine has nothing to remember the shed from, and refuses `--pick` and `--root`. The agent-box container's init script went from a per-agent loop with hard-coded state paths to one command.
8
+ - `status` no longer counts `~/.claude/skills/synced` as a part outside the shed. Claude Code 2.1 creates that folder itself, one bucket per account, for the skills it syncs from claude.ai — a single `claude -p` on a fresh machine is enough to make it appear — so every restored machine showed `outside 1: skills/synced → lshed add`, and following the hint would have copied the account's skills into the shed. The folder is now skipped in the scan, like a hidden directory; `init`, `add` and `status` all read the same scan. Found by running a fresh-machine test in a container.
9
+ - The VM probe now runs in a local container as well. `scripts/vm/Dockerfile.probe` bakes the runbook's image (Node 22, Codex, Gemini CLI, Copilot CLI, Cursor CLI, Antigravity CLI, and lshed from the checkout) with the unchanged `install-tools.sh`, and `scripts/vm/probe-docker.sh build` / `probe-docker.sh <target>...` runs `probe.sh` in it, passing through whichever agent keys the environment holds and falling back to the model-free run when there are none. The tools that were waiting for a cloud VM (Gemini, Copilot, Cursor) can now be checked on any machine with Docker; the first run (2026-09-18, Gemini CLI 0.60.0, Copilot CLI 1.0.86, Cursor agent 2026.09.15) passed every placement, format, link and cleanup check for all three, and `gemini mcp list` accepted the `settings.json` lshed wrote. With free-tier keys the same day, Gemini and Cursor answered every model question on the first attempt; only Copilot's questions still wait for a token. A `probe` workflow (weekly and on demand) builds that image on a GitHub runner and runs the six Linux targets, model-free unless the keys are set as repository secrets, so a change to an adapter or an MCP form is checked against the tools' own parsers instead of only against lshed's tests. One probe fix on the way: `gemini mcp list` is now run with `GEMINI_CLI_TRUST_WORKSPACE=true`, as the questions already were, because Gemini suppresses user-level servers in an untrusted folder and the probe's scratch folder is one.
10
+ - Windows: an argument holding a `%` is now refused instead of being passed through. `cmd.exe` expands `%VAR%` even inside double quotes and then reads the result again, and there is no way to spell a literal `%` on a command line it parses — so 0.17.3's quoting wrapped it and hoped. Nothing lshed sends that way can legitimately contain one (a package id is letters, digits and `._@-`; the check prompt is fixed), while a shed's plugin source is free text, so refusing costs nothing and closes the last gap in the shell work.
11
+ - `lshed check` clears the temporary folders it could not remove before. On Windows a CLI that leaves a child behind keeps its working directory open for that child's lifetime, so the removal fails at the time and the folder stays; the next check now sweeps any `lshed-check-*` older than an hour out of the temp directory before it starts. The age cut leaves a check that is running right now alone, and the message about a folder that is still held says it will be cleared later rather than asking for it to be removed by hand.
12
+ - The package tests get the timeout the git-heavy blocks in `sync.test.ts` already use. They run on real repositories — the fixture clones a bare remote for every test, and each test adds its own clones, commits and pushes — and `windows-latest` on Node 20 went past the 5s default on a test that had taken 2.4s a run earlier. No behaviour changed; this only stops a slow runner from reading as a failure.
13
+
3
14
  ## 0.17.4 — 2026-09-11
4
15
 
5
16
  - An `install:` command that failed left no trace once the restore finished. The parts were placed, the profile was recorded, and the one mention of the failure scrolled past — `lshed status` then showed the machine as `packages 1 in sync`, `drift none`, exactly like a machine where everything worked. The failing package ids are now kept in the machine's state, `status` prints them under the package they belong to (`! gstack install: failed at the last restore → fix it, then lshed restore default --yes`), and `lshed report` names them. A later restore that succeeds clears the record.
package/README.ko.md CHANGED
@@ -9,39 +9,9 @@ lshed init --shed ~/lshed # ~/.claude 를 스캔해 창고에 담고 ls
9
9
  lshed restore research # 어디서든 프로필 적용
10
10
  ```
11
11
 
12
- 창고는 그냥 디렉터리입니다. git 저장소에 두든 Dropbox에 두든 상관없고, git 부분은 `lshed sync`가 대신해 줍니다. Claude Code 기준으로 만들었지만 같은 창고를 Codex, Gemini CLI, Copilot CLI, Cursor, Google Antigravity, 공용 `~/.agents/skills`에도 놓을 수 있습니다.
12
+ 창고는 그냥 디렉터리입니다. git 저장소에 두든 Dropbox에 두든 상관없고, git 부분은 원하면 `lshed sync`가 대신해 줍니다. Claude Code 기준으로 만들었지만 같은 창고를 Codex, Gemini CLI, Copilot CLI, Cursor, Google Antigravity, 공용 `~/.agents/skills`에도 놓을 수 있습니다.
13
13
 
14
- ## 왜 필요한가
15
-
16
- 새 노트북, 서버, 컨테이너, WSL을 하나 늘릴 때마다 `~/.claude`를 다시 꾸며야 합니다. 가장 뻔한 해법은 `~/.claude` 자체를 git에 넣는 것이고, 많은 사람에게는 그것이 정답입니다.
17
-
18
- **`~/.claude`를 그냥 git 저장소로 두면 충분한 경우.** 혼자 쓰고, 모든 기기가 같은 구성이며, `~/.claude` 안의 것이 전부 직접 만든 것이라면 `~/.claude` 자체를 git 저장소로 만들면 끝나고, lshed를 설치할 이유가 없습니다. 옮기는 것은 git이고, `.gitignore`는 세션 기록·캐시 같은 기기 상태값을 저장소에서 빼는 제외 목록일 뿐입니다.
19
-
20
- ```
21
- cd ~/.claude && git init
22
- printf 'projects/\ncache/\nsessions/\nshell-snapshots/\nhistory.jsonl\n*.bak*\n' > .gitignore
23
- git add skills agents commands CLAUDE.md settings.json .gitignore && git commit -m init
24
- ```
25
-
26
- 새로 배울 개념도, 복사 단계도 없습니다. 편집하는 디렉터리가 곧 저장소이고, 여기서 `git push`한 뒤 다음 기기에서 `git clone <원격> ~/.claude`하는 것이 곧 복원입니다.
27
-
28
- **그것이 막히는 지점.** `~/.claude`가 "내가 쓴 것"만이 아니게 되는 순간 위 저장소는 아프기 시작합니다.
29
-
30
- - **설치한 툴킷.** 툴킷 하나가 1.6 GB짜리 clone이고, 직접 쓴 스킬 4개 옆에 별칭 스킬 53개를 만들어 놓습니다. 날것의 git은 이것을 전부 커밋하거나, 아니면 ignore 목록을 손으로 관리해야 합니다.
31
- - **JSON 파일 하나 안의 시크릿.** MCP 서버와 토큰이 `~/.claude.json` 안에 무관한 상태값과 함께 있습니다. 파일 단위 ignore로는 가를 수 없어, 토큰을 커밋하거나 MCP를 포기해야 합니다.
32
- - **이미 설정이 있는 기기.** 비어 있지 않은 `~/.claude`에 `git clone`하는 것은 손으로 하는 병합이고, 어떤 파일이 저장소에서 왔고 어떤 것이 원래 있던 것인지 아무도 기억하지 않습니다.
33
- - **기기마다 다른 부분집합.** 헤드리스 서버에는 브라우저 툴킷도 MCP도 필요 없습니다. 브랜치나 템플릿으로 흉내낼 수는 있지만, 구성을 바꿀 때 더 이상 원치 않는 부품을 치워 주는 것은 없습니다.
34
- - **기기에서 직접 고르기.** `git clone`은 전부 아니면 전무입니다. 새 기기에서는 창고에 뭐가 있는지 카테고리별로 보고, 이 기기에 필요한 것만 체크하고 싶습니다.
35
-
36
- lshed는 이 다섯 경우를 위해 있습니다. 창고는 여전히 git 안의 평범한 디렉터리이고, 그 위에 개념 셋을 얹습니다.
37
-
38
- | 개념 | 얻는 것 |
39
- |---|---|
40
- | **부품(component)** | 스킬·에이전트·명령·지침 조각·MCP 서버·설정 키 하나하나가 이름 있는 부품입니다. 설치한 툴킷은 복사하지 않고 출처와 버전만 기록합니다. |
41
- | **프로필(profile)** | `research`, `work`, `minimal`처럼 이름 붙인 조합입니다. `lshed.yaml`에 적거나, `restore --pick`의 체크리스트로 만듭니다. |
42
- | **관리 집합(managed set)** | lshed는 자기가 놓은 것을 기억하므로, 프로필을 바꾸거나 기존 기기에 복원해도 자기 파일만 치우고 여러분의 파일은 건드리지 않습니다. |
43
-
44
- 대가도 있습니다. 편집은 `~/.claude`에서 하고 `lshed save`로 창고에 복사해야 하며(많이 편집하는 기기에서는 `restore --link`로 복사 단계를 없앨 수 있습니다), lshed는 각 에이전트의 파일 배치를 알아야 합니다. 다섯 경우 중 하나도 해당하지 않으면 그냥 git 저장소가 낫습니다.
14
+ **목차:** [설치](#설치) · [빠른 시작](#빠른-시작) · [그냥 git 으로는 안 되나?](#그냥-git-으로는-안-되나) · [사용법](#사용법) · [레퍼런스](#레퍼런스) · [신뢰](#신뢰) · [문제 해결](#문제-해결) · [검증된 것](#검증된-것)
45
15
 
46
16
  ## 설치
47
17
 
@@ -69,7 +39,7 @@ macOS와 Linux에서는 먼저 `chmod +x`가 필요합니다. 서명하지 않
69
39
  # 1. 이미 설정이 있는 기기에서
70
40
  lshed init --shed ~/lshed # Claude Code 기준. 다른 도구는 --agent codex|gemini|copilot|cursor|agy
71
41
  cd ~/lshed && git init && git remote add origin <비공개 저장소>
72
- lshed sync # commit + push
42
+ lshed sync # 창고 커밋, pull, push
73
43
 
74
44
  # 2. ~/lshed/lshed.yaml 을 열어 프로필을 만들고, 모든 기기에 필요하지 않은 부품은 빼세요
75
45
 
@@ -80,7 +50,35 @@ lshed restore --pick --shed ~/lshed # 또는 카테고리별로 이 기
80
50
  lshed check # 선택: 놓인 것을 에이전트가 읽는지 물어본다
81
51
  ```
82
52
 
83
- 다른 기기가 다른 에이전트를 쓰면 거기서 `restore` 에 `--agent` 를 붙이세요. 스킬·지침 파일·MCP 서버는 그대로 옮겨지고 나머지는 알리고 건너뜁니다([자세히](#다른-에이전트도-같은-창고로)).
53
+ 다른 기기가 다른 에이전트를 쓰면 거기서 `restore` 에 `--agent` 를 붙이세요. 스킬·지침 파일·MCP 서버는 그대로 옮겨지고 나머지는 알리고 건너뜁니다([자세히](#다른-에이전트도-같은-창고로)). 시작할 때마다 도는 컨테이너나 dotfiles 스크립트에 넣으려면 [새 기기](#새-기기)를 보세요.
54
+
55
+ ## 그냥 git 으로는 안 되나?
56
+
57
+ 혼자 쓰고, 모든 기기가 같은 구성이며, `~/.claude` 안의 것이 전부 직접 만든 것이라면 `~/.claude` 자체를 git 저장소로 만들고 lshed 는 건너뛰세요.
58
+
59
+ ```
60
+ cd ~/.claude && git init
61
+ printf 'projects/\ncache/\nsessions/\nshell-snapshots/\nhistory.jsonl\n*.bak*\n' > .gitignore
62
+ git add skills agents commands CLAUDE.md settings.json .gitignore && git commit -m init
63
+ ```
64
+
65
+ 그것은 `~/.claude`가 "내가 쓴 것"만이 아니게 되는 순간 막힙니다.
66
+
67
+ - **설치한 툴킷** — 툴킷 하나가 1.6 GB짜리 clone이고, 직접 쓴 스킬 4개 옆에 별칭 스킬 53개를 만들어 놓습니다. 날것의 git은 이것을 전부 커밋하거나, 아니면 ignore 목록을 손으로 관리해야 합니다.
68
+ - **JSON 파일 하나 안의 시크릿** — MCP 서버와 토큰이 `~/.claude.json` 안에 무관한 상태값과 함께 있습니다. 토큰을 커밋하거나 MCP를 포기해야 합니다.
69
+ - **이미 설정이 있는 기기** — 비어 있지 않은 `~/.claude`에 `git clone`하는 것은 손으로 하는 병합이고, 어떤 파일이 저장소에서 왔는지 아무도 기억하지 않습니다.
70
+ - **기기마다 다른 부분집합** — 헤드리스 서버에는 브라우저 툴킷도 MCP도 필요 없고, 구성을 바꿀 때 더 이상 원치 않는 부품을 치워 주는 것은 없습니다.
71
+ - **기기에서 직접 고르기** — `git clone`은 전부 아니면 전무입니다.
72
+
73
+ lshed는 창고를 git 안의 평범한 디렉터리로 두고, 그 위에 개념 셋을 얹습니다.
74
+
75
+ | 개념 | 얻는 것 |
76
+ |---|---|
77
+ | **부품(component)** | 스킬·에이전트·명령·지침 조각·MCP 서버·설정 키 하나하나가 이름 있는 부품입니다. 설치한 툴킷은 복사하지 않고 출처와 버전만 기록합니다. |
78
+ | **프로필(profile)** | `research`, `work`, `minimal`처럼 이름 붙인 조합입니다. `lshed.yaml`에 적거나, `restore --pick`의 체크리스트로 만듭니다. |
79
+ | **관리 집합(managed set)** | lshed는 자기가 놓은 것을 기억하므로, 프로필을 바꾸거나 기존 기기에 복원해도 자기 파일만 치우고 여러분의 파일은 건드리지 않습니다. |
80
+
81
+ 대가도 있습니다. 편집은 `~/.claude`에서 하고 `lshed save`로 창고에 복사해야 합니다(많이 편집하는 기기에서는 `restore --link`로 복사 단계를 없앨 수 있습니다).
84
82
 
85
83
  ## 사용법
86
84
 
@@ -109,12 +107,12 @@ lshed.yaml written: /home/me/lshed/lshed.yaml (4 parts, 3 packages, 53 generate
109
107
 
110
108
  ```
111
109
  cd ~/lshed && git init && git remote add origin git@github.com:me/harness.git
112
- lshed sync
110
+ lshed sync # 창고 커밋, pull, push
113
111
  ```
114
112
 
115
113
  ### 반복: 편집, 저장, 동기화
116
114
 
117
- 스킬은 에이전트가 읽는 자리, 곧 그 도구의 폴더(`~/.claude`, `~/.codex`, `~/.gemini` 등)에서 편집합니다. 창고는 저절로 바뀌지 않습니다.
115
+ 스킬은 에이전트가 읽는 자리, 곧 그 도구의 폴더에서 편집합니다. 창고는 저절로 바뀌지 않습니다.
118
116
 
119
117
  ```
120
118
  lshed status # 적용된 프로필, 드리프트, 새로 생긴 것
@@ -123,7 +121,7 @@ lshed save # 로컬 편집을 창고로 (또는: lshed save skills/ad
123
121
  lshed sync # 창고 커밋, pull, push
124
122
  ```
125
123
 
126
- 이 명령들에는 `--agent` 가 필요 없습니다. lshed 상태가 있는 에이전트가 하나뿐인 기기에서는 그것을 찾고, 여럿이면 어느 것인지 물어봅니다. 에이전트 폴더에서 창고로 가는 길은 `save`뿐이고, 창고가 소유한 부품(`file:` 출처)에만 동작합니다. `sync`는 저장하지 않은 편집이 있으면 경고해서, 기기보다 뒤처진 창고를 push하지 않게 합니다.
124
+ 이 명령들에는 `--agent` 가 필요 없습니다. lshed 상태가 있는 에이전트가 하나뿐인 기기에서는 그것을 찾고, 여럿이면 어느 것인지 물어봅니다. 에이전트 폴더에서 창고로 가는 길은 `save`뿐이고, 창고가 소유한 부품에만 동작합니다. `sync`는 저장하지 않은 편집이 있으면 경고해서, 기기보다 뒤처진 창고를 push하지 않게 합니다.
127
125
 
128
126
  ### 이미 설정이 있는 기기
129
127
 
@@ -143,16 +141,16 @@ $ lshed restore default --shed ~/lshed --dry-run
143
141
  ```
144
142
  lshed add # 창고에 없는 것을 나열
145
143
  lshed add windows-only mcp/my-local-server
146
- lshed sync
144
+ lshed sync # 창고 커밋, pull, push
147
145
  ```
148
146
 
149
- 둘러보려고 그런 기기에서 `init`을 돌리지 마세요. `init`은 찾은 것을 lshed 관리 대상으로 등록하므로, 나중에 진짜 창고로 `restore`하면 그 부품들을 제거 대상으로 봅니다(백업은 되지만 제거됩니다). 출력만 하는 `lshed scan`을 쓰세요. 이미 그랬다면 `restore`가 제거 전에 경고하고, `lshed add`가 빠져나오는 길입니다.
147
+ 둘러보려고 그런 기기에서 `init`을 돌리지 마세요. `init`은 찾은 것을 lshed 관리 대상으로 등록하므로, 나중에 진짜 창고로 `restore`하면 그 부품들을 제거 대상으로 봅니다(백업은 되지만 제거됩니다). 출력만 하는 `lshed scan`을 쓰세요.
150
148
 
151
149
  ### 새 기기
152
150
 
153
151
  ```
154
152
  git clone git@github.com:me/harness.git ~/lshed
155
- lshed restore default --shed ~/lshed
153
+ lshed restore default --shed ~/lshed # 또는 2 에서 만든 프로필. --shed 는 처음 한 번만
156
154
  ```
157
155
 
158
156
  ```
@@ -174,7 +172,26 @@ Some entries need environment variables that are not set. Secrets never go in th
174
172
  mcp:notion: NOTION_AUTHORIZATION
175
173
  ```
176
174
 
177
- 그 뒤 손이 가는 것은 둘입니다. 패키지의 `install:`은 clone해 온 저장소의 셸 명령이므로 `restore`는 보여 주고 멈춥니다. 직접 돌리거나 `--yes`로 다시 실행하세요. `--yes`로 돌린 명령이 실패해도 복원은 멈추지 않습니다. 부품은 그대로 놓이고, 실패는 끝에 모아 보여 주며, `restore`는 exit 1로 끝납니다. MCP 서버는 시크릿을 `${VAR}`로 참조하니 셸에서 export하면 Claude Code가 채웁니다. 이후로는 인자 없는 `lshed restore`가 마지막 프로필을 다시 적용하고, 창고 위치도 기억합니다.
175
+ 그 뒤 손이 가는 것은 둘입니다. 패키지의 `install:`은 clone해 온 저장소의 셸 명령이므로 `restore`는 보여 주고 멈춥니다. 직접 돌리거나 `--yes`로 다시 실행하세요(`--yes`로 돌린 명령이 실패해도 복원은 멈추지 않습니다. 부품은 그대로 놓이고, 실패는 끝에 모아 보여 주며, `restore`는 exit 1로 끝납니다). MCP 서버는 시크릿을 `${VAR}`로 참조하니 셸에서 export하면 에이전트가 채웁니다. 이후로는 인자 없는 `lshed restore`가 마지막 프로필을 다시 적용하고, 창고 위치도 기억합니다.
176
+
177
+ **컨테이너와 dotfiles 스크립트.** 시작할 때마다 도는 부트스트랩에는 플래그 둘로 한 줄이 됩니다. `--fresh-only` 는 이미 lshed 상태가 있는 루트에서는 아무것도 하지 않고(exit 0, 그렇다는 한 줄만), `--agent installed` 는 `PATH` 에 CLI 가 있는 모든 에이전트에 차례로, 각자의 상태로 복원합니다.
178
+
179
+ ```
180
+ lshed restore default --shed ~/lshed --agent installed --fresh-only
181
+ ```
182
+
183
+ ```
184
+ Agents with a CLI here: claude-code, codex, agy
185
+
186
+ ── claude-code ──
187
+ + package gstack (clone https://github.com/garrytan/gstack.git @main → c8f0c4e)
188
+ …
189
+ ── codex ──
190
+ · codex does not handle settings, skipped
191
+ …
192
+ ```
193
+
194
+ `installed` 는 `claude`, `codex`, `gemini`, `copilot`, `agent`(Cursor), `agy` 를 찾습니다. 폴더 규약만 있는 `agents` 대상은 Codex 가 `~/.agents/skills` 를 이미 채우므로 빠집니다. 새 기기에는 창고를 기억할 상태가 없으므로 `--shed` 나 `LSHED_HOME` 이 필요하고, `installed` 는 `--pick` 이나 `--root` 와 함께 쓸 수 없습니다. 어느 에이전트의 복원이든 실패하면 전부 시도한 뒤 exit 1 로 끝납니다. 정확히 이것을 하는 devcontainer — 에이전트와 lshed 가 든 이미지, `/shed` 에 마운트한 창고, entrypoint 의 이 명령 — 가 [agent-box](https://github.com/LeeSongHeon-LSH/agent-box) 에 있습니다.
178
195
 
179
196
  ### 프로필 이름 대신 골라서 넣기
180
197
 
@@ -210,9 +227,9 @@ Profile "lab-box" saved to lshed.yaml. To use it on other machines, push it with
210
227
  Profile "lab-box" applied: placed 4, removed 0, installed 1 package
211
228
  ```
212
229
 
213
- 창고에 아무것도 없는 카테고리(여기서는 `agents`, `commands`, `settings`)는 빈 화면 대신 건너뜁니다. 선택은 반드시 프로필로 저장되며, 이름을 따로 치지 않으면 기기 이름이 됩니다. 그래야 다음 번 인자 없는 `lshed restore`가 같은 것을 다시 적용하고, `lshed sync`가 다른 기기로 실어 나릅니다. 같은 이름의 프로필이 이미 있으면 덮어쓰기 전에 묻습니다.
230
+ 창고에 아무것도 없는 카테고리는 빈 화면 대신 건너뜁니다. 선택은 반드시 프로필로 저장되며, 이름을 따로 치지 않으면 기기 이름이 됩니다. 그래야 다음 번 인자 없는 `lshed restore`가 같은 것을 다시 적용하고, `lshed sync`가 다른 기기로 실어 나릅니다. 같은 이름의 프로필이 이미 있으면 덮어쓰기 전에 묻습니다.
214
231
 
215
- `lshed restore default --pick`은 `default`의 부품이 체크된 채 시작하므로, 빈 손에서 시작하는 대신 이 기기용으로 덜어낼 수 있습니다. 인자가 없으면 마지막 적용 프로필이 출발점입니다. `--dry-run`은 계획만 보여 주고 `lshed.yaml`도 `~/.claude`도 쓰지 않습니다. 어느 화면에서든 Ctrl+C는 아무것도 바꾸지 않습니다. 적용된 프로필이 없는 기기에서 터미널에서 `lshed restore --shed ~/lshed`만 치면 picker가 저절로 열립니다. 스크립트나 파이프에서는 대신 프로필 이름을 요구합니다.
232
+ `lshed restore default --pick`은 `default`의 부품이 체크된 채 시작하므로, 빈 손에서 시작하는 대신 이 기기용으로 덜어낼 수 있습니다. `--dry-run`은 계획만 보여 주고 `lshed.yaml`도 에이전트 폴더도 쓰지 않습니다. 어느 화면에서든 Ctrl+C는 아무것도 바꾸지 않습니다. 적용된 프로필이 없는 기기에서 터미널에서 `lshed restore --shed ~/lshed`만 치면 picker가 저절로 열립니다. 스크립트나 파이프에서는 대신 프로필 이름을 요구합니다.
216
233
 
217
234
  ### 프로필
218
235
 
@@ -240,7 +257,7 @@ lshed restore server
240
257
  + lshed/instructions/server-rules.md
241
258
  ```
242
259
 
243
- 전환은 이전 프로필이 놓은 것만 치우고(`-`), 둘 다 쓰는 것은 두고(`=`), 내용이 바뀐 것만 다시 씁니다(`~`). 치우거나 덮어쓰는 것은 모두 `~/.claude/lshed/backups/<시각>/`에 먼저 갑니다. 패키지는 더하기만 합니다. `gstack`을 적지 않은 프로필도 clone은 그대로 둡니다. `--dry-run`은 이 계획만 출력합니다. 지침 조각은 순서가 있습니다. `restore`는 조각을 `@`-import하는 `CLAUDE.md`를 만들므로, 창고에서 조각을 고치면 다음 `restore`에 반영되고 병합할 것이 없습니다.
260
+ 전환은 이전 프로필이 놓은 것만 치우고(`-`), 둘 다 쓰는 것은 두고(`=`), 내용이 바뀐 것만 다시 씁니다(`~`). 치우거나 덮어쓰는 것은 모두 `~/.claude/lshed/backups/<시각>/`에 먼저 갑니다. 패키지는 더하기만 합니다. `gstack`을 적지 않은 프로필도 clone은 그대로 둡니다. 지침 조각은 순서가 있습니다. `restore`는 조각을 `@`-import하는 `CLAUDE.md`를 만들므로, 창고에서 조각을 고치면 다음 `restore`에 반영되고 병합할 것이 없습니다.
244
261
 
245
262
  프로필은 `extends`로 다른 프로필 위에 쌓을 수 있어, 기기별 프로필에는 다른 점만 적으면 됩니다.
246
263
 
@@ -258,17 +275,17 @@ profiles:
258
275
  instructions: [lab-rules] # default 의 main 뒤에 옴
259
276
  ```
260
277
 
261
- 상속은 더하기만 합니다. 부모의 부품이 먼저, 자기 것이 뒤에 오고, 지침도 그 순서로 `CLAUDE.md`에 들어갑니다. 부모보다 *적게* 가지려면 상속하지 말고 원하는 것을 직접 적으세요. 없는 부모나 순환은 아무것도 건드리기 전에 `lshed.yaml` 오류로 알리고, `lshed list`는 상속받는 프로필도 그 부품을 쓰는 것으로 셉니다.
278
+ 상속은 더하기만 합니다. 부모의 부품이 먼저, 자기 것이 뒤에 옵니다. 부모보다 *적게* 가지려면 상속하지 말고 원하는 것을 직접 적으세요. 없는 부모나 순환은 아무것도 건드리기 전에 `lshed.yaml` 오류로 알립니다.
262
279
 
263
280
  헷갈리기 쉬운 세 가지가 있는데, 일부러 나눠 두었습니다.
264
281
 
265
- - **안 쓰되 남겨 두기** — 그 id 를 프로필에서 빼기만 합니다. `restore` 가 이 기계에서 치우고(백업) 부품은 창고에 남아, 다른 프로필은 계속 쓰고, id 를 다시 넣어 restore 하면 돌아옵니다. 되돌릴 수 있는 일상적 "제거"이고, 이걸 위해 창고에서 무엇을 뺄 필요는 없습니다.
266
- - **상속받은 목록을 줄이기** — `extends` 는 더하기만 하므로, 프로필을 상속하면서 그중 한 부품만 빼는 것은 안 됩니다. 작은 목록을 그냥 나열하거나, 공용 base 를 갈라 그것을 상속하세요. "빼기가 없다"는 말은 오직 이 뜻입니다.
267
- - **창고에서 부품을 삭제하기** — `lshed remove <id>` (어떤 프로필이 아직 그 부품을 나열하면 거부하니 먼저 프로필에서 빼세요) 또는 아무 프로필도 안 쓰는 것을 치우는 `lshed prune`. 영구적이며, 창고에서 파일을 실제로 없애는 유일한 경우입니다.
282
+ - **안 쓰되 남겨 두기** — 그 id 를 프로필에서 빼기만 합니다. `restore` 가 이 기기에서 치우고(백업) 부품은 창고에 남아, 다시 적으면 돌아옵니다. 되돌릴 수 있는 일상적 "제거"입니다.
283
+ - **상속받은 목록을 줄이기** — `extends` 는 더하기만 하므로, 프로필을 상속하면서 그중 한 부품만 빼는 것은 안 됩니다. 작은 목록을 그냥 나열하거나, 공용 base 를 갈라 그것을 상속하세요.
284
+ - **창고에서 부품을 삭제하기** — `lshed remove <id>` (어떤 프로필이 아직 그 부품을 나열하면 거부) 또는 아무 프로필도 안 쓰는 것을 치우는 `lshed prune`. 영구적인 쪽입니다.
268
285
 
269
286
  ### 다른 에이전트도 같은 창고로
270
287
 
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` 중 하나로만 넣으세요.
288
+ Codex, Gemini CLI, Copilot CLI, Cursor, Google Antigravity(`agy`)는 모두 `<자기 설정 디렉터리>/skills/<이름>/SKILL.md`를 읽습니다. Claude Code와 같은 배치이고, 대부분은 공용 `~/.agents/skills/`도 읽습니다. 그래서 창고 하나로 전부를 채울 수 있고, 대상은 `--agent`로 고릅니다. Codex의 스킬은 Codex 문서가 정한 위치인 `~/.agents/skills`에 놓으므로, 한 기기에서 스킬은 `--agent codex`와 `--agent agents` 중 하나로만 넣으세요.
272
289
 
273
290
  ```
274
291
  lshed restore --agent agents # ~/.agents/skills: 규약을 따르는 모든 도구가 읽는 공용 위치
@@ -280,20 +297,20 @@ lshed restore --agent agy # ~/.gemini/config/skills + ~/.gemini/AG
280
297
  | `--agent` | 루트 | 스킬 | 지침 파일 | MCP 서버 |
281
298
  |---|---|---|---|---|
282
299
  | `claude-code` (기본) | `~/.claude` 또는 `$CLAUDE_CONFIG_DIR` | 됨, agents·commands·settings 도 | `CLAUDE.md`, 조각을 `@`-import | `~/.claude.json` |
283
- | `codex` | `~/.codex` 또는 `$CODEX_HOME` | 됨, `~/.agents/skills`에 (Codex 문서가 정한 위치; `~/.codex/skills`는 deprecated) | `AGENTS.md`, 조각을 이어 붙임 | `config.toml` `[mcp_servers.*]` |
300
+ | `codex` | `~/.codex` 또는 `$CODEX_HOME` | 됨, `~/.agents/skills`에 | `AGENTS.md`, 조각을 이어 붙임 | `config.toml` `[mcp_servers.*]` |
284
301
  | `gemini` | `~/.gemini` | 됨 | `GEMINI.md`, 이어 붙임 | `settings.json` |
285
302
  | `copilot` | `~/.copilot` 또는 `$COPILOT_HOME` | 됨 | `copilot-instructions.md`, 이어 붙임 | `mcp-config.json` |
286
303
  | `cursor` | `~/.cursor` | 됨 | 없음 (Cursor 의 사용자 규칙은 설정 UI 안에 있음) | `mcp.json` |
287
- | `agy` | `~/.gemini/config` | 됨 | `../AGENTS.md`, 이어 붙임 (agy 는 `GEMINI.md`도 읽지만 그건 Gemini CLI 의 것) | `mcp_config.json` |
304
+ | `agy` | `~/.gemini/config` | 됨 | `../AGENTS.md`, 이어 붙임 | `mcp_config.json` |
288
305
  | `agents` | `~/.agents` | 됨 | 없음 | 없음 |
289
306
 
290
- MCP 항목은 창고에 Claude Code 형식으로 두고 나갈 때 바꿉니다. Gemini는 `type` 없이 `httpUrl`, Antigravity는 `serverUrl`, Copilot은 `type: local`과 `tools: ["*"]`, Cursor는 `${env:VAR}` 자리표시자, Codex는 `env_vars` / `bearer_token_env_var` / `env_http_headers`에 변수 *이름*을 적습니다. 그래서 Cursor와 Codex의 설정 파일에는 시크릿 값이 아예 들어가지 않습니다. Gemini, Antigravity, Copilot은 자리표시자를 스스로 채우지 않으므로 `restore`가 셸 환경에서 채웁니다. Codex의 `config.toml`은 해당 표만 골라 고치므로 주석과 다른 설정은 그대로입니다. `lshed init --agent gemini`처럼 반대 방향도 되며, 시크릿은 마스킹돼 창고에 들어갑니다.
307
+ MCP 항목은 창고에 Claude Code 형식으로 두고 나갈 때 각 도구의 형식으로 바꿉니다. Codex와 Cursor의 설정 파일에는 변수 *이름*만 들어가므로 시크릿 값이 아예 들어가지 않고, Gemini, Antigravity, Copilot은 자리표시자를 스스로 채우지 않으므로 `restore`가 셸 환경에서 채웁니다. Codex의 `config.toml`은 해당 표만 골라 고치므로 주석과 다른 설정은 그대로입니다.
291
308
 
292
- 에이전트 루트마다 자기 `lshed/state.json`이 있어, `~/.codex`에 복원해도 `~/.claude`에 놓은 것은 건드리지 않고, 루트마다 다른 프로필이나 `--link` 선택을 가질 수 있습니다. 대상이 모르는 부품은 알리고 건너뜁니다. 설정 키가 든 프로필을 Codex에 복원하면 `codex 은 settings 를 다루지 않아 건너뜁니다`라는 줄과 함께 나머지만 놓습니다. Claude 플러그인 패키지도 같은 식으로 건너뛰지만, `github:`/`git:` 패키지는 그 프로필을 복원하는 모든 에이전트 루트에 clone되므로, 원치 않으면 다른 에이전트용으로 그것이 없는 프로필을 두세요. `lshed init --agent codex`도 되고, Codex에서 만든 창고를 Claude Code로 복원하면 같은 조각으로 `CLAUDE.md`가 생성됩니다. 창고의 `agent:`는 `--agent`의 기본값일 뿐입니다(`$LSHED_AGENT`도 됩니다).
309
+ 에이전트 루트마다 자기 `lshed/state.json`이 있어, `~/.codex`에 복원해도 `~/.claude`에 놓은 것은 건드리지 않고, 루트마다 다른 프로필이나 `--link` 선택을 가질 수 있습니다. 대상이 모르는 부품은 알리고 건너뜁니다(다른 에이전트에서는 설정과 Claude 플러그인). `github:`/`git:` 패키지는 그 프로필을 복원하는 모든 에이전트 루트에 clone되므로, 원치 않으면 다른 에이전트용으로 그것이 없는 프로필을 두세요. `lshed init --agent codex`도 되고, Codex에서 만든 창고를 Claude Code로 복원하면 같은 조각으로 `CLAUDE.md`가 생성됩니다. 창고의 `agent:`는 `--agent`의 기본값일 뿐입니다(`$LSHED_AGENT`도 됩니다).
293
310
 
294
311
  ### 복사 대신 링크
295
312
 
296
- 편집을 주로 하는 기기에서는 `restore --link`가 스킬·에이전트·명령·지침 조각을 복사본 대신 창고를 가리키는 링크로 놓습니다. `~/.claude`에서 한 편집이 곧바로 창고에 있으므로 `diff`는 보고할 것이 없고 `save`는 할 일이 없습니다. `lshed sync`가 순환의 전부입니다.
313
+ 편집을 주로 하는 기기에서는 `restore --link`가 스킬·에이전트·명령·지침 조각을 복사본 대신 창고를 가리키는 링크로 놓습니다. 편집이 곧바로 창고에 있으므로 `diff`는 보고할 것이 없고 `save`는 할 일이 없습니다. `lshed sync`가 순환의 전부입니다.
297
314
 
298
315
  ```
299
316
  $ lshed restore --link
@@ -305,7 +322,7 @@ $ lshed restore --link
305
322
  Profile "default" applied (link): placed 4, removed 0
306
323
  ```
307
324
 
308
- 선택은 기기별이고 기억됩니다. 그 기기의 이후 `lshed restore`도 계속 링크로 놓고, `lshed status`는 `placement links`를 보여 주며, `restore --no-link`로 복사로 돌아갑니다. 다른 기기에는 영향이 없습니다. MCP 항목과 설정 키는 파일이 아니라 JSON 값이라 항상 씁니다. 프로필을 바꾸면 링크만 지우고 그 뒤의 창고는 절대 지우지 않습니다. Windows에서는 디렉터리가 특별한 권한 없이 junction이 되고, 파일 하나짜리 부품(에이전트, 명령, 조각)은 링크에 개발자 모드가 필요합니다. 없으면 복사하고 그렇다고 알린 뒤 보통 복사본처럼 다룹니다(`save`도 됩니다).
325
+ 선택은 기기별이고 기억됩니다. 그 기기의 이후 `lshed restore`도 계속 링크로 놓고, `lshed status`는 `placement links`를 보여 주며, `restore --no-link`로 복사로 돌아갑니다. MCP 항목과 설정 키는 파일이 아니라 JSON 값이라 항상 씁니다. 프로필을 바꾸면 링크만 지우고 그 뒤의 창고는 절대 지우지 않습니다. Windows에서는 디렉터리가 특별한 권한 없이 junction이 되고, 파일 하나짜리 부품은 링크에 개발자 모드가 필요합니다. 없으면 복사하고 그렇다고 알린 뒤 보통 복사본처럼 다룹니다.
309
326
 
310
327
  ### 나중에 추가하기
311
328
 
@@ -326,20 +343,18 @@ $ lshed add paper-review mcp/linear
326
343
  2 added to the shed and to profile "default". Commit the shed: /home/me/lshed
327
344
  ```
328
345
 
329
- `add`는 `init`과 똑같이 분류하고, 주석을 흐트러뜨리지 않고 `lshed.yaml`에 덧붙이며, 현재 프로필과 관리 집합에 넣습니다. 키 없이 부르면 나열만 합니다. `status`는 그 수를 `outside` 행에 보여 줍니다. 이미 창고에 있는 부품을 다른 프로필에 넣는 것은 `profiles:`를 직접 고치는 일이고, 그런 경우 `add`가 알려 줍니다.
346
+ `add`는 `init`과 똑같이 분류하고, 주석을 흐트러뜨리지 않고 `lshed.yaml`에 덧붙이며, 현재 프로필에 넣습니다. 키 없이 부르면 나열만 합니다. `status`는 그 수를 `outside` 행에 보여 줍니다. 이미 창고에 있는 부품을 다른 프로필에 넣는 것은 `profiles:`를 직접 고치는 일입니다.
330
347
 
331
348
  ### 패키지 최신으로 유지하기
332
349
 
333
350
  ```
334
- lshed status # clone 이 움직였으면 "! gstack 253d1df ≠ lock 0d1bd56 → lshed update" 로 알림
351
+ lshed status 적용 프로필, 드리프트, 패키지, 없는 환경변수, 새것
335
352
  lshed update --dry-run # 업스트림에 물어만 보고 아무것도 안 바꿈
336
353
  lshed update # 프로필의 모든 패키지를 당기고 lshed.lock 갱신
337
354
  lshed update gstack --yes # 하나만, 그리고 install: 실행
338
355
  ```
339
356
 
340
- git 패키지는 `lshed.lock`에 커밋으로 고정되어 새 기기도 정확히 그 커밋을 받습니다. 플러그인은 고정할 수 없으므로 lock에는 설치된 버전을 적고, 이전 기기와 다르면 `status`가 알려 줍니다.
341
-
342
- `update --dry-run`은 아무것도 바꾸지 않고 업스트림만 읽습니다. clone이 이미 원격 끝에 있으면 `= (최신)`, pull이 옮길 것이면 `~ 0d1bd56 → 0530392`, 미리 알 수 없으면 `?`입니다. git 패키지와 Claude Code가 git으로 받은 마켓플레이스는 확인할 수 있고, Claude 플러그인과 공식 마켓플레이스(git clone이 아님)는 알 수 없어 `?`로 표시되며 실제 `update`만이 답합니다.
357
+ git 패키지는 `lshed.lock`에 커밋으로 고정되어 새 기기도 정확히 그 커밋을 받습니다. 플러그인은 고정할 수 없으므로 lock에는 설치된 버전을 적고, 다르면 `status`가 알려 줍니다. `update --dry-run`은 git 패키지는 확인할 수 있고, Claude 플러그인은 `?`로 표시되며 실제 `update`만이 답합니다. 갱신할 수 없는 패키지는 알리고 건너뛰며, 나머지는 당겨 옵니다.
343
358
 
344
359
  ### 정리
345
360
 
@@ -361,13 +376,38 @@ lshed prune --yes # 안 쓰는 것 전부 삭제
361
376
  | `~` | 다른 내용이 있어 교체함 (백업) |
362
377
  | `-` | 제거함 (백업) 또는 제외 |
363
378
  | `≡` | 패키지: 출처만 기록, 복사 안 함 |
364
- | `·` | 설치기가 만든 것, 건너뜀 |
379
+ | `·` | 설치기가 만든 것, 또는 이 에이전트가 다루지 않는 것: 건너뜀 |
365
380
  | `!` | 확인 필요 |
366
381
  | `↑` `↓` | push / pull (sync), 갱신 (update) |
367
382
 
368
383
  오류는 stderr로 나가고 종료 코드는 1입니다. 나머지는 전부 stdout입니다.
369
384
 
370
- ## 매니페스트
385
+ ## 레퍼런스
386
+
387
+ ### 명령
388
+
389
+ ```
390
+ lshed init [--shed <dir>] [--profile <name>] [--exclude <id...>]
391
+ lshed add [keys...] [--all] init 뒤에 생긴 것을 창고로
392
+ lshed restore [profile] [--pick] [--link | --no-link] [--dry-run] [--no-backup] [--yes] [--fresh-only] (--agent <name> 으로 다른 도구 대상, --agent installed 는 CLI 가 있는 도구 전부)
393
+ lshed status 적용 프로필, 드리프트, 패키지, 없는 환경변수, 새것
394
+ lshed diff 로컬과 창고가 다른 파일(또는 JSON 키)
395
+ lshed save [ids...] 로컬 편집을 창고로
396
+ lshed sync [-m <msg>] [--no-push] [--dry-run] 창고 커밋, pull --rebase, push
397
+ lshed update [ids...] [--dry-run] [--yes] 패키지 당기고 lshed.lock 갱신, --dry-run 은 업스트림에 묻기만
398
+ lshed list [--unused] 창고의 내용과 그것을 쓰는 프로필
399
+ lshed remove <key> 창고에서 부품이나 패키지 삭제
400
+ lshed prune [--yes] 어느 프로필도 안 쓰는 것 전부 삭제
401
+ lshed scan 루트를 읽기만 하고 나열
402
+ lshed check [--attempts <n>] [--timeout <s>] 방금 놓은 스킬을 에이전트 CLI 가 읽는지 물어본다 (작은 모델 호출 한두 번)
403
+ lshed report [--open | --url] 이슈에 붙여 넣을 이 설정의 요약, --open 은 GitHub 이슈 폼에 채워서 열고 --url 은 그 링크만 찍는다
404
+ ```
405
+
406
+ 키는 `카테고리/id`이고, 모호하지 않으면 `id`만 써도 됩니다: `skills/paper-review`, `mcp/exa`, `packages/gstack`. id는 에이전트가 읽는 파일·디렉터리 이름 그대로이며 어느 문자 체계든 됩니다(`skills/논문리뷰`). 글자, 숫자, `.`, `_`, `-`, 그리고 하위 폴더에 둔 에이전트·명령을 위한 구간 사이 `/`가 허용됩니다.
407
+
408
+ 공통 옵션: `--shed <dir>`(또는 `LSHED_HOME`, 첫 restore 뒤에는 기억함), `--agent <name>`(또는 `LSHED_AGENT`, 기본은 창고의 `agent:`, 그다음 `claude-code`; `restore` 에서 `installed` 는 PATH 에 CLI 가 있는 모든 에이전트), `--root <dir>`(에이전트 설정 루트, 기본은 에이전트 자체 위치).
409
+
410
+ ### 매니페스트
371
411
 
372
412
  `lshed.yaml`은 창고 루트에 있습니다. `init`이 만들고, 그다음부터는 직접 고칩니다. `add`와 `remove`도 주석을 보존하며 고쳐 줍니다.
373
413
 
@@ -415,31 +455,31 @@ profiles:
415
455
  ```
416
456
 
417
457
  - 부품의 `source`는 `file:<창고 기준 상대 경로>`입니다. 패키지의 `source`는 `github:owner/repo@ref`, `git:<url>#ref`, `claude-marketplace:<owner/repo>`, `claude-plugin:<name>@<marketplace>`를 받습니다.
418
- - 카테고리 이름은 어댑터가 정합니다. Claude Code는 `skills`, `agents`, `commands`, `instructions`, `mcp`, `settings`이고, 다른 에이전트는 `skills`에 더해 위 표에 있는 대로 `instructions` / `mcp`를 가집니다.
458
+ - Claude Code의 카테고리는 `skills`, `agents`, `commands`, `instructions`, `mcp`, `settings`이고, 다른 에이전트는 `skills`에 더해 [위 표](#다른-에이전트도-같은-창고로)에 있는 대로 `instructions` / `mcp`를 가집니다.
419
459
  - `ignore:`는 기본 목록(`node_modules`, `.git`, `__pycache__`, `.venv`, 캐시 디렉터리, `*.log`)에 더해집니다. `dist/` 같은 빌드 산출물은 스킬에 따라 필요하므로 기본으로는 빼지 않습니다.
420
460
  - `exclude:`는 로컬에는 있지만 창고에 들어가면 안 되는 부품입니다. `init --exclude`가 적어 줍니다.
421
461
 
422
- ## 세 종류의 것
462
+ ### 세 종류의 것
423
463
 
424
464
  실제 `~/.claude`에는 세 종류가 섞여 있고, 각각 다르게 다뤄야 합니다.
425
465
 
426
466
  | 종류 | 예 | lshed 가 하는 일 |
427
467
  |---|---|---|
428
- | **직접 만든 것** | 직접 쓴 스킬, `CLAUDE.md`, 직접 넣은 MCP 서버 | 창고에 복사 |
429
- | **설치한 것** | `git clone`한 툴킷, 플러그인 | 출처와 커밋만 기록, `restore`가 다시 clone·설치 |
468
+ | **직접 만든 것** | 직접 쓴 스킬, `CLAUDE.md`, 직접 넣은 MCP 서버 | 창고에 복사, `save`가 편집을 되가져옴 |
469
+ | **설치한 것** | `git clone`한 툴킷, 플러그인 | 출처와 커밋만 기록, `restore`가 다시 clone·설치, `update`가 당겨 옴 |
430
470
  | **설치가 만든 것** | 설치기가 만들어 둔 스텁 스킬 | 건너뜀, 설치기를 돌리면 돌아옴 |
431
471
 
432
472
  `.git`과 remote가 있는 디렉터리는 **패키지**가 됩니다. 심볼릭 링크가 패키지 안을 가리키는 스킬은 생성물로 보고 건너뜁니다. 나머지는 직접 만든 것으로 보고 복사합니다. 이것을 안전하게 지키는 규칙은 다음과 같습니다.
433
473
 
434
- - 이미 있는 패키지는 `restore`가 절대 건드리지 않습니다. 로컬 체크아웃은 여러분 것입니다. 예외는 `--yes` 하나로, 패키지의 `install:`을 다시 돌립니다. 첫 restore 뒤의 "rerun with `--yes`" 안내가 말 그대로 되도록요.
435
- - `install:`은 셸 명령입니다. `--yes`가 없으면 `restore`와 `update`는 **보여 주고 멈춥니다.** 플러그인 설치는 Claude Code 자체 패키지 관리자를 거치므로 그 없이도 돌고, 설치 명령을 선언한 플러그인에는 `--yes`가 `-y`로 전달됩니다.
474
+ - 이미 있는 패키지는 `restore`가 절대 건드리지 않습니다. 로컬 체크아웃은 여러분 것입니다. 예외는 `--yes` 하나로, 패키지의 `install:`을 다시 돌립니다.
475
+ - `install:`은 셸 명령입니다. `--yes`가 없으면 `restore`와 `update`는 **보여 주고 멈춥니다.** 플러그인 설치는 Claude Code 자체 패키지 관리자를 거치므로 그 없이도 돕니다.
436
476
  - 패키지는 관리 집합에 들어가지 않습니다. 프로필을 바꿔도 clone은 지워지지 않습니다.
437
477
 
438
478
  Claude Code 플러그인은 자기 스킴을 가진 패키지입니다. `init`은 `~/.claude/plugins`의 사용자 범위 플러그인을 찾고, `restore`는 마켓플레이스를 먼저 추가한 뒤 `claude plugin install`을 돌립니다. 프로젝트 범위 플러그인은 그 프로젝트의 것이라 기록하지 않습니다.
439
479
 
440
- ## MCP 서버와 시크릿
480
+ ### MCP 서버와 시크릿
441
481
 
442
- 사용자 범위 MCP 서버는 `~/.claude.json` 안에 기기 ID, 세션 상태와 함께 있습니다. lshed는 서버 하나를 `mcp` 카테고리의 부품 하나로 봅니다. 창고에는 `mcp/<이름>.json`이 있고, `restore`는 `~/.claude.json`의 `mcpServers.<이름>` 키만 고치고 나머지는 그대로 둡니다.
482
+ 사용자 범위 MCP 서버는 `~/.claude.json` 안에 기기 ID, 세션 상태와 함께 있습니다. lshed는 서버 하나를 `mcp` 카테고리의 부품 하나로 봅니다. 창고에는 `mcp/<이름>.json`이 있고, `restore`는 `mcpServers.<이름>` 키만 고치고 그 파일의 나머지는 그대로 둡니다.
443
483
 
444
484
  **시크릿 값은 창고에 들어가지 않습니다.** `init`과 `add`는 `env`와 `headers` 아래에서 키 이름에 시크릿처럼 보이는 단어(`key`, `token`, `secret`, `password`, `auth`, `authorization`, `credential`, `cookie`, `session` — 단어 단위라 `MAX_OUTPUT_TOKENS`는 해당 없음)가 있는 값을 `${VAR}` 자리표시자로 바꿉니다.
445
485
 
@@ -450,57 +490,26 @@ Claude Code 플러그인은 자기 스킴을 가진 패키지입니다. `init`
450
490
  "headers": { "Authorization": "Bearer ${NOTION_AUTHORIZATION}" } }
451
491
  ```
452
492
 
453
- `restore`는 자리표시자를 그대로 씁니다. Claude Code가 서버를 띄울 때 환경에서 `${VAR}`를 채우므로 값은 셸에만 있습니다. 예외는 `${HOME}` 하나로, 이것은 lshed가 직접 채웁니다. Claude Code는 환경에 있는 변수만 채우는데 Windows에는 `HOME`이 없고, 없으면 "Missing environment variables: HOME"을 내며 서버를 띄우지 않기 때문입니다(2.1.265로 확인)(`~/.zshrc`의 `export EXA_API_KEY=...` 등, 시크릿을 관리하는 방식대로). `restore`와 `status`는 프로필에 필요한데 설정되지 않은 변수를 나열합니다. 휴리스틱은 제안일 뿐입니다. 창고의 JSON을 고쳐 자리표시자를 더하거나 빼도 되고, `args`나 `url`에 토큰처럼 보이는 것이 있으면 `init`이 경고합니다. `save`는 기존 자리표시자를 유지하고 새로 생긴 시크릿 키를 마스킹하므로, 교체한 키가 실수로 창고에 새지 않습니다. `diff`는 자리표시자를 와일드카드로 비교해서, 실제 값을 가진 기기가 드리프트로 잡히지 않습니다.
493
+ `restore`는 자리표시자를 그대로 씁니다. Claude Code가 서버를 띄울 때 환경에서 `${VAR}`를 채우므로 값은 셸에만 있습니다(`~/.zshrc`의 `export EXA_API_KEY=...` 등, 시크릿을 관리하는 방식대로). 예외는 `${HOME}` 하나로, Windows에는 `HOME`이 없으므로 lshed가 직접 채웁니다. `restore`와 `status`는 프로필에 필요한데 설정되지 않은 변수를 나열합니다. 휴리스틱은 제안일 뿐입니다. 창고의 JSON을 고쳐 자리표시자를 더하거나 빼세요. `save`는 기존 자리표시자를 유지하고 새로 생긴 시크릿 키를 마스킹하므로 교체한 키가 실수로 창고에 새지 않고, `diff`는 자리표시자를 와일드카드로 비교해서 실제 값을 가진 기기가 드리프트로 잡히지 않습니다.
454
494
 
455
- ## 설정
495
+ ### 설정
456
496
 
457
497
  `~/.claude/settings.json`에는 훅, 권한, `env`, 모델, 테마, 그리고 Claude Code가 스스로 쓰는 상태값이 있습니다. lshed는 이 파일을 병합하지 않습니다. **최상위 키 하나가 `settings` 카테고리의 부품 하나**입니다. 창고에는 `settings/permissions.json`, `settings/hooks.json` 같은 파일이 있고, `restore`는 딱 그 키만 쓰고 나머지는 둡니다. 프로필은 `permissions`와 `hooks`만 실어 나르고 `model`은 기기마다 다르게 둘 수 있습니다.
458
498
 
459
- ```
460
- $ lshed add
461
- 3 outside the shed:
462
- settings/hooks ! points into package gstack. If that install created it, exclude it: settings/hooks
463
- settings/model
464
- settings/theme
465
- ```
466
-
467
499
  - `enabledPlugins`와 `extraKnownMarketplaces`는 담지 않습니다. 플러그인·마켓플레이스 패키지의 몫이고, `restore`가 설치하면서 다시 만듭니다.
468
- - 홈 아래 절대 경로는 창고에서 `${HOME}/…`가 되어, 한 기기에서 쓴 훅 명령이 다른 기기에서도 돕니다. `C:\Users\me\.claude\hooks\x`로 썼든 `/home/me/.claude/hooks/x`로 썼든 창고에서는 `${HOME}` 뒤가 늘 `/`라서, 창고 하나가 Windows·WSL·macOS·Linux를 함께 섬깁니다. Windows에서 `restore`는 이를 `C:/Users/me/.claude/hooks/x`로 되돌리며, 이 형태는 Node, PowerShell, cmd, Git Bash가 모두 읽습니다. Claude Code는 `settings.json`의 변수를 채우지 않으므로 `restore`가 `${HOME}`과 `${VAR}`를 셸에서 직접 채우고, 없는 변수는 알린 뒤 자리표시자로 둡니다.
469
- - `env`는 시크릿 맵으로 봅니다. 시크릿처럼 보이는 키는 마스킹하고 나머지(`CLAUDE_CODE_MAX_OUTPUT_TOKENS` 등)는 그대로 갑니다.
500
+ - 홈 아래 절대 경로는 창고에서 늘 `/`를 쓰는 `${HOME}/…`가 되어, 한 기기에서 쓴 훅 명령이 다른 기기에서도 돕니다. Windows에서 `restore`는 이를 `C:/Users/me/…`로 되돌리며, 이 형태는 Node, PowerShell, cmd, Git Bash가 모두 읽습니다. Claude Code는 `settings.json`의 변수를 채우지 않으므로 `restore`가 `${HOME}`과 `${VAR}`를 셸에서 직접 채웁니다.
501
+ - `env`는 시크릿 맵으로 봅니다. 시크릿처럼 보이는 키는 마스킹하고 나머지는 그대로 갑니다.
470
502
  - 패키지 안을 가리키는 값(툴킷 설치기가 쓴 훅)은 표시됩니다. 설치기가 다시 만들어 주는 것이면 `exclude:`에 넣고 `restore --yes`가 되살리게 두세요.
471
503
  - 창고가 키 전체를 소유하므로, 로컬에서 추가한 권한은 `diff`에 나타나고 다른 편집처럼 `save`로 창고에 들어갑니다.
472
504
 
473
- ## 명령 레퍼런스
474
-
475
- ```
476
- lshed init [--shed <dir>] [--profile <name>] [--exclude <id...>]
477
- lshed add [keys...] [--all] init 뒤에 생긴 것을 창고로
478
- lshed restore [profile] [--pick] [--link | --no-link] [--dry-run] [--no-backup] [--yes] (--agent <name> 으로 다른 도구 대상)
479
- lshed status 적용 프로필, 드리프트, 패키지, 없는 환경변수, 새것
480
- lshed diff 로컬과 창고가 다른 파일(또는 JSON 키)
481
- lshed save [ids...] 로컬 편집을 창고로
482
- lshed sync [-m <msg>] [--no-push] [--dry-run] 창고 커밋, pull --rebase, push
483
- lshed update [ids...] [--dry-run] [--yes] 패키지 당기고 lshed.lock 갱신, --dry-run 은 업스트림에 묻기만
484
- lshed list [--unused] 창고의 내용과 그것을 쓰는 프로필
485
- lshed remove <key> 창고에서 부품이나 패키지 삭제
486
- lshed prune [--yes] 어느 프로필도 안 쓰는 것 전부 삭제
487
- lshed scan 루트를 읽기만 하고 나열
488
- lshed check [--attempts <n>] [--timeout <s>] 방금 놓은 스킬을 에이전트 CLI 가 읽는지 물어본다 (작은 모델 호출 한두 번)
489
- lshed report [--open | --url] 이슈에 붙여 넣을 이 설정의 요약, --open 은 GitHub 이슈 폼에 채워서 열고 --url 은 그 링크만 찍는다
490
- ```
491
-
492
- 키는 `카테고리/id`이고, 모호하지 않으면 `id`만 써도 됩니다: `skills/paper-review`, `mcp/exa`, `packages/gstack`. id는 에이전트가 읽는 파일·디렉터리 이름 그대로이며 어느 문자 체계든 됩니다(`skills/논문리뷰`). 글자, 숫자, `.`, `_`, `-`, 그리고 하위 폴더에 둔 에이전트·명령을 위한 구간 사이 `/`가 허용됩니다.
493
-
494
- 공통 옵션: `--shed <dir>`(또는 `LSHED_HOME`, 첫 restore 뒤에는 기억함), `--agent <name>`(또는 `LSHED_AGENT`, 기본은 창고의 `agent:`, 그다음 `claude-code`), `--root <dir>`(에이전트 설정 루트, 기본은 에이전트 자체 위치인 `~/.claude`나 `~/.codex` 등).
495
-
496
505
  ### `restore` 가 하는 일
497
506
 
498
507
  0. 프로필의 패키지 중 없는 것을 `lshed.lock`의 버전으로 설치합니다.
499
508
  1. **이전** 프로필이 놓았고 새 프로필에는 없는 경로를 치웁니다.
500
- 2. 새 프로필의 부품을 제자리에 복사합니다(`--link`이거나 전에 링크를 쓴 기기라면 창고로 가는 링크로). MCP 항목은 `~/.claude.json`에, 설정 키는 `settings.json`에 씁니다.
509
+ 2. 새 프로필의 부품을 제자리에 복사합니다(`--link`이거나 전에 링크를 쓴 기기라면 링크로). MCP 항목과 설정 키를 씁니다.
501
510
  3. 지침 파일을 다시 만듭니다.
502
511
 
503
- 덮어쓰거나 치우는 것은 `--no-backup`이 없는 한 먼저 `~/.claude/lshed/backups/<시각>/`에 백업합니다. lshed가 놓은 적 없는 파일은 건드리지 않습니다. `--dry-run`은 계획만 출력합니다. `--pick`이면 비어 있지 않은 카테고리마다 체크리스트가 먼저 나옵니다(packages, skills, agents, commands, instructions, MCP 서버, 설정 키). `[profile]`을 주면 그 프로필의 부품이 미리 체크됩니다. 결과는 `lshed.yaml`에 프로필로 저장되고, 그 프로필로 0~3단계가 돕니다.
512
+ 덮어쓰거나 치우는 것은 `--no-backup`이 없는 한 먼저 `<루트>/lshed/backups/<시각>/`에 백업합니다. lshed가 놓은 적 없는 파일은 건드리지 않습니다. `--dry-run`은 계획만 출력합니다. `--pick`이면 비어 있지 않은 카테고리마다 체크리스트가 먼저 나오고, 결과가 `lshed.yaml`에 프로필로 저장된 뒤 0~3단계가 돕니다.
504
513
 
505
514
  ### `sync` 가 하는 일
506
515
 
@@ -511,69 +520,69 @@ lshed report [--open | --url] 이슈에 붙여 넣을 이 설
511
520
 
512
521
  충돌이 나면 rebase를 중단하고, 커밋은 그대로 둔 채 창고를 깨끗하게 남기고, git으로 해결하라고 알립니다. remote가 없으면 커밋만 합니다. `save`를 대신 돌리지는 않습니다.
513
522
 
514
- ### 소유권
515
-
516
- 직접 만든 부품의 진실은 창고입니다. `save`는 `file:` 부품의 로컬 편집을 창고로 되가져오고, 링크된 부품은 곧 창고입니다. 패키지의 주인은 upstream입니다. `update`가 당겨 오고, `save`는 건드리지 않습니다.
517
-
518
- ### 신뢰
519
-
520
- 창고는 그냥 데이터가 아니라 실행됩니다. `restore`는 에이전트 설정에 파일을 놓고, 자신이 쓰는 설정의 `${VAR}`를 당신 셸의 값으로 채우며, `--yes`면 각 패키지의 `install:` 셸 명령을 실행합니다. 그러니 자신의 dotfiles만큼 신뢰하는 창고만 `restore`하고, 당신 것은 비공개로(비공개 git 저장소) 두세요. 남의 창고를 `restore`하는 것은 그 사람에게 당신의 기계를 내주는 것에 가깝습니다 — 매니페스트에 `install:` 명령이 있을 수 있고, 그것이 쓰는 설정이 MCP 서버를 어떤 URL로 향하게 해 당신의 비밀 하나를 그리로 보낼 수 있습니다. lshed는 자신이 놓는 것을 에이전트 루트 안으로 가두고 git 옵션을 몰래 넣을 수 있는 패키지 출처를 거부하지만, 창고의 `install:`이 무엇을 돌리는지, 설정이 비밀을 어디로 보내는지까지 보증하지는 못합니다. 당신이 쓰지 않은 창고는 곧 돌리려는 남의 코드처럼 다루세요.
521
-
522
- ## 어디에 무엇이 있나
523
+ ### 어디에 무엇이 있나
523
524
 
524
525
  ```
525
- <창고>/
526
- lshed.yaml 매니페스트
527
- lshed.lock 패키지 버전 (생성됨)
526
+ <shed>/
527
+ lshed.yaml manifest
528
+ lshed.lock package versions (generated)
528
529
  skills/<id>/ agents/<id>.md commands/<id>.md instructions/<id>.md
529
- mcp/<id>.json 시크릿은 ${VAR}
530
- settings/<id>.json 최상위 키 하나씩, 홈 경로는 ${HOME}
530
+ mcp/<id>.json secrets as ${VAR}
531
+ settings/<id>.json one top-level key each; home paths as ${HOME}
531
532
 
532
533
  ~/.claude/
533
- skills/ agents/ commands/ CLAUDE.md ← restore 가 놓음
534
- settings.json <id> ← 부품마다 키 하나, 나머지는 그대로
535
- lshed/state.json ← 어느 프로필, 어느 경로를 관리하는지
536
- lshed/instructions/<id>.md ← CLAUDE.md 가 import 하는 조각
537
- lshed/backups/<시각>/ ← restore 가 교체한 것
534
+ skills/ agents/ commands/ CLAUDE.md ← placed by restore
535
+ settings.json <id> ← one key per settings component; the rest is untouched
536
+ lshed/state.json ← which profile, which paths are managed
537
+ lshed/instructions/<id>.md ← fragments imported by CLAUDE.md
538
+ lshed/backups/<timestamp>/ ← whatever restore replaced
538
539
  ~/.claude.json mcpServers.<id> ← 부품마다 키 하나, 나머지는 그대로
539
540
 
540
541
  ~/.codex/ ~/.gemini/ ~/.copilot/ ~/.cursor/ ~/.gemini/config/ ~/.agents/
541
- skills/ <지침 파일> <mcp 파일> ← --agent 별로 같은 구조 (위 표 참고; codex 의 스킬은 ~/.agents/skills)
542
- lshed/state.json lshed/backups/ ← 루트마다 따로
542
+ skills/ <지침 파일> <mcp 파일> ← --agent 별로 같은 구조 (codex 의 스킬은 ~/.agents/skills)
543
+ lshed/state.json lshed/backups/ ← each root keeps its own
543
544
  ```
544
545
 
545
546
  `state.json`은 기기별이고 창고에 들어가지 않습니다. `CLAUDE_CONFIG_DIR`가 있으면 lshed는 그것을 루트로 쓰고, Claude Code처럼 `.claude.json`도 그 안에 씁니다.
546
547
 
547
- ## 검증된 것
548
+ ## 신뢰
548
549
 
549
- - 테스트, CLI 스모크, 단독 실행파일이 push마다 **Ubuntu, macOS, Windows**(Node 20, 22)에서 돕니다. Windows는 `--link`에 junction을, 플러그인 설치에 `claude.cmd`를 씁니다. 스모크에는 한글 이름 스킬이 들어 있어, macOS의 파일 이름 정규화 차이나 Windows의 코드페이지 문제는 사용자 기기가 아니라 CI에서 먼저 실패합니다.
550
- - 개발자 모드가 꺼진 실제 Windows 11 PC(PowerShell 7과 cmd.exe, Node 24, 공백과 한글이 든 경로)에서도 npm 으로 설치한 lshed 0.15.1 로 같은 절차가 통과했습니다. `init`, 이미 설정이 있는 기기로의 `restore`, `--link`(스킬은 junction, 단일 파일은 안내와 함께 복사), `add`/`diff`/`save`, 프로필 전환, `codex`·`agents` 대상, 원격 없는 `sync`, 스모크까지입니다. 같은 PC 에서 두 차례 더 확인했습니다. 0.15.2 는 거기서 만든 창고가 WSL 안에서 `/home/…/` 경로로 복원되고 옛 창고가 `save` 한 번으로 정규화되는 것을, 0.15.3 은 링크 모드가 복사로 놓았던 파일 부품을 `=` 로 안정시키고 창고가 바뀔 때만 다시 복사하는 것을 확인했습니다.
551
- - 같은 PC 에서 다섯 번째로(Windows PowerShell 5.1, Node 24, git 2.55, npm 으로 설치한 lshed 0.17.1) 빈 상태부터 다시 걸었습니다. private 창고를 HTTPS 로 clone 하고 `restore default` 를 돌리자 모든 패키지가 락에 적힌 리비전으로 왔고(Linux 기기에서 올린 gstack 업데이트가 같은 커밋으로 도착), `status` 는 아홉 패키지 모두 in sync, `lshed check` 는 첫 시도에 Claude Code 가 암호어를 돌려줬으며, `report` 는 홈 디렉터리를 `~` 로 가렸습니다. 이 검증이 `restore --yes` 가 첫 `install:` 실패(gstack 의 `./setup` 은 cmd.exe 에서 시작조차 못 함)에서 아무것도 놓지 않고 멈추는 것을 찾았고, 0.17.2 가 고친 뒤 같은 PC 에서 확인했습니다. 실패는 찍히고, 부품 아홉은 놓이고, 실패한 명령은 끝에 목록으로 나옵니다.
552
- - 개발하는 Linux 기기에서는 CI 너머까지 전체 명령을 돌려 봅니다. `install:`이 있는 git·GitHub 패키지의 `restore`와 `update`, 충돌까지 포함한 실제 원격과의 `sync`, `remove`/`prune`, 모든 `--agent` 대상, 환경변수 기본값, 실제 터미널을 거친 `restore --pick`, 컴파일된 Linux 바이너리까지입니다.
553
- - 다른 에이전트는 문서만이 아니라 도구 자체로 확인합니다. `scripts/vm/probe.sh`는 임시 창고를 도구의 실제 루트에 복원한 뒤, 스킬에 든 암호어, 지침 파일에 든 코드워드, `--link` 링크를 거친 같은 스킬을 비대화형으로 물어봅니다. Codex 0.153.4와 Antigravity CLI 1.1.27은 모든 검사를 통과했고(마지막 실행 2026-09-09, lshed 0.15.5), Gemini CLI·Copilot CLI·Cursor는 아직 파일 배치와 형식까지만 확인했습니다. 자세한 내용과 새 VM에서 전부 돌리는 cloud-init 파일은 `scripts/vm/README.md`에 있습니다.
554
- - 개발하는 기기에서는 실제 창고 하나를 매일 씁니다. Claude Code, Codex, Antigravity가 모두 `--link`로 그 창고를 읽고, 셋 다 `status`에 드리프트가 없습니다.
555
- - 위는 전부 한 사람의 기기입니다. 여기 없는 도구 버전이나 OS 에서 lshed 가 잘 돌았다면 [검증 보고](https://github.com/LeeSongHeon-LSH/lshed/issues/new?template=verified.yml)를 남겨 주세요. 2분이면 되고, 이 절의 한 줄이 됩니다. 안 돌았다면 `lshed report` 가 [버그 보고](https://github.com/LeeSongHeon-LSH/lshed/issues/new?template=bug.yml)에 필요한 것을 찍어 줍니다. 버전, 에이전트와 루트, 적용 프로필, 창고에 든 것의 이름까지이고 값이나 시크릿은 없으며 홈 디렉터리는 `~` 로 나옵니다. 명령이 실패한 직후에는 그 요약을 채운 폼을 열지 물어봅니다. lshed 가 스스로 보내는 것은 없고, `LSHED_REPORT=0` 이면 묻지 않습니다. 소리 없이 실패하는 경우, 즉 에이전트가 놓인 것을 그냥 안 읽는 경우는 `lshed check` 가 에이전트에게 직접 물어봅니다.
556
-
557
- ## 아직 범위 밖
558
-
559
- - "변수 이름만 적는" 것 이상의 시크릿 처리. 암호화된 값, `op://` 참조, OS 키체인은 나중 일이고, 지금은 일부러 dotfiles보다 나을 것이 없게 두었습니다.
560
- - 프로젝트 범위 MCP 서버(`.mcp.json`, `~/.claude.json`의 `projects.*`)와 프로젝트 범위 플러그인. 프로젝트의 것입니다.
561
- - 다른 에이전트는 스킬, 지침 파일, MCP 서버만 옮깁니다. 각 도구의 설정 파일, 규칙 폴더, 플러그인은 그대로 둡니다.
550
+ 창고는 그냥 데이터가 아니라 실행됩니다. `restore`는 에이전트 설정에 파일을 놓고, 자신이 쓰는 설정의 `${VAR}`를 당신 셸의 값으로 채우며, `--yes`면 각 패키지의 `install:` 셸 명령을 실행합니다. 그러니 자신의 dotfiles만큼 신뢰하는 창고만 `restore`하고, 당신 것은 비공개로 두세요. 남의 창고를 `restore`하는 것은 그 사람에게 당신의 기계를 내주는 것에 가깝습니다 — 매니페스트에 `install:` 명령이 있을 수 있고, 그것이 쓰는 설정이 MCP 서버를 어떤 URL로 향하게 해 당신의 비밀 하나를 그리로 보낼 수 있습니다. lshed는 자신이 놓는 것을 에이전트 루트 안으로 가두고 git 옵션을 몰래 넣을 수 있는 패키지 출처를 거부하지만, 창고의 `install:`이 무엇을 돌리는지, 설정이 비밀을 어디로 보내는지까지 보증하지는 못합니다. 당신이 쓰지 않은 창고는 곧 돌리려는 남의 코드처럼 다루세요.
562
551
 
563
552
  ## 문제 해결
564
553
 
565
554
  - **restore 는 다 놓았다는데 에이전트가 못 본다** — `lshed check`. 임의 암호어가 든 임시 스킬을 에이전트의 스킬 폴더에 놓고, 사용자가 하듯 그 CLI 에 비대화형으로 암호어를 물은 뒤(`claude -p`, `codex exec`, `gemini -p`, `copilot -p`, `agent -p`, `agy -p`) 스킬을 치웁니다. ✔ 면 위치와 형식은 맞고 문제는 다른 데 있는 것이고, ✘ 면 에이전트의 실제 답이 찍히는데 그것이 버그 보고에 딱 필요한 것입니다. 작은 모델 호출 한두 번이 들고, 그 CLI 가 이 기기에 있어야 합니다.
566
- - **다른 문제가 생겼다** — `lshed report` 가 버그 보고에 필요한 요약을 찍습니다(비밀은 없지만 직접 확인하세요). `lshed report --open` 은 그것을 새 이슈 폼에 채워 엽니다(브라우저가 없는 기기에서는 `--url` 이 링크만 찍습니다). 명령이 실패한 직후에도 같은 것을 묻는데, 아니오라고 하거나 `LSHED_REPORT=0` 을 두면 아무 일도 없습니다.
555
+ - **다른 문제가 생겼다** — `lshed report` 가 버그 보고에 필요한 요약을 찍습니다(비밀은 없지만 직접 확인하세요). `lshed report --open` 은 그것을 새 이슈 폼에 채워 엽니다(`--url` 은 링크만 찍습니다). 명령이 실패한 직후에도 같은 것을 묻는데, 아니오라고 하거나 `LSHED_REPORT=0` 을 두면 아무 일도 없습니다.
567
556
  - **"Shed location unknown. Pass --shed <dir> or set LSHED_HOME."** (창고 위치를 모름) — `--shed <dir>`를 주거나 `LSHED_HOME`을 설정하세요. `restore`가 한 번 성공하면 기억합니다.
568
557
  - **restore가 내 `CLAUDE.md`를 바꿨다** — `~/.claude/lshed/backups/<시각>/CLAUDE.md`에 있습니다. 내용을 창고의 조각으로 옮기고 그 조각을 프로필에 넣으세요.
569
558
  - **로컬에서 고친 스킬을 지키고 싶다** — `lshed diff`로 보고, `lshed save <id>`로 창고에 넣고, `lshed sync`.
570
559
  - **`status`가 패키지가 lock과 다르다고 한다** — 무언가가 lshed 몰래 clone이나 플러그인을 갱신했습니다(Claude Code는 플러그인을 자동 갱신합니다). `lshed update`가 새 버전을 기록합니다.
571
560
  - **`status`가 같은 새 항목을 계속 보여 준다** — 설치기 별칭이거나 임시 파일입니다. `lshed.yaml`의 `exclude:`에 넣으세요.
572
- - **restore가 MCP 변수가 없다고 한다** — 셸 프로필에서 export하고 Claude Code를 다시 시작하세요. `~/.claude.json`의 자리표시자는 맞게 들어간 것이고, Claude Code가 시작할 때 채웁니다.
573
- - **restore가 훅 경로를 엉뚱하게 썼다** — 창고는 홈 경로를 `${HOME}/…`로 담습니다. 이 기기에서 다른 곳을 가리켜야 하면 창고의 JSON을 `${HOME}`이나 다른 변수로 고치고 다시 `restore`하세요. 홈 밖의 경로(`D:\tools\x.exe`, `/opt/x`)는 쓴 그대로 옮겨지며, 휴대성은 사용자 몫입니다.
574
- - **Windows에서 `restore --yes`가 install 명령이 실패했다고 한다** — Windows에서 `install:`은 cmd.exe로 돌기 때문에 sh용으로 쓴 `./setup`은 시작조차 못 합니다(`'.' is not recognized`). 나머지는 다 놓였으니 Git Bash에서 그 명령을 직접 돌리고(`cd ~/.claude/skills/<패키지> && ./setup`), 패키지 자체의 요구 사항도 확인하세요(gstack은 bun이 필요합니다).
561
+ - **`status` 에 `! <package> install: failed at the last restore` 가 보인다** — 부품은 놓였고, 그 패키지의 `install:` 만 끝나지 않은 것입니다. 필요한 것을 갖춘 뒤 `lshed restore <profile> --yes` 를 하거나, 찍힌 명령을 직접 돌리세요.
562
+ - **restore가 MCP 변수가 없다고 한다** — 셸 프로필에서 export하고 에이전트를 다시 시작하세요. 설정 파일의 자리표시자는 맞게 들어간 것이고, 에이전트가(자리표시자를 채우지 않는 도구라면 lshed가) 채웁니다.
563
+ - **restore가 훅 경로를 엉뚱하게 썼다** — 창고는 홈 경로를 `${HOME}/…`로 담습니다. 이 기기에서 다른 곳을 가리켜야 하면 창고의 JSON을 `${HOME}`이나 다른 변수로 고치고 다시 `restore`하세요. 홈 밖의 경로는 쓴 그대로 옮겨지며, 휴대성은 사용자 몫입니다.
564
+ - **Windows에서 `restore --yes`가 install 명령이 실패했다고 한다** — Windows에서 `install:`은 cmd.exe로 돌기 때문에 sh용으로 쓴 `./setup`은 시작조차 못 합니다(`'.' is not recognized`). 나머지는 다 놓였으니 Git Bash에서 그 명령을 직접 돌리고(`cd ~/.claude/skills/<package> && ./setup`), 패키지 자체의 요구 사항도 확인하세요(gstack은 bun이 필요합니다).
575
565
  - **Windows에서 `--link`가 파일을 복사했다** — 파일 하나짜리 링크는 개발자 모드가 필요합니다. 켜고 다시 `restore`하거나, 복사본을 그대로 두세요. 이후 `restore`는 그것을 `= … (copy; …)`로 알리고 건드리지 않으며, 편집은 `save`로 되가져옵니다.
576
- - **sync가 충돌로 멈췄다** — `cd <창고> && git pull --rebase`, 해결, `git rebase --continue`, 그리고 다시 `lshed sync`.
566
+ - **sync가 충돌로 멈췄다** — `cd <shed> && git pull --rebase`, 해결, `git rebase --continue`, 그리고 다시 `lshed sync`.
567
+ - **부트스트랩 스크립트가 시작할 때마다 `restore` 를 돌린다** — `--fresh-only` 를 붙이세요. [새 기기](#새-기기)를 보세요.
568
+
569
+ ## 검증된 것
570
+
571
+ | 어디서 | 무엇을 |
572
+ |---|---|
573
+ | Ubuntu, macOS, Windows (CI, push 마다) | 단위 테스트, 전 절차를 도는 CLI 스모크(한글 이름 스킬 포함), 단독 실행파일 |
574
+ | 개발자 모드가 꺼진 Windows 11 PC | npm 설치본과 소스 빌드로 여섯 차례 직접 검증: `init`, 이미 설정이 있는 기기로의 `restore`, `--link`(junction), 프로필 전환, `codex`/`agents` 대상, `check`, `report`, `sync` |
575
+ | Linux (매일 사용) | Claude Code, Codex, Antigravity 가 `--link` 로 읽는 실제 창고 하나; `sync` 충돌과 `--pick` 을 포함해 CI 너머의 모든 명령 |
576
+ | 에이전트 자체 | probe 가 임시 창고를 복원한 뒤 각 도구에 스킬에 든 암호어, 지침 파일의 코드워드, `--link` 를 거친 같은 스킬을 물어봅니다. Claude Code, Codex, Gemini CLI, Cursor, Antigravity 는 답하고, Copilot CLI 는 아직 배치와 형식까지 확인했습니다. 주간 워크플로가 현재 CLI 들에 대해 배치 부분을 반복합니다 |
577
+ | 완전히 새 기기 | 빈 상태에서 시작한 devcontainer 가 entrypoint 에서 세 에이전트에 복원하고 각각 `status`, `check`, `mcp list` 를 통과하며, 재시작 때는 복원을 건너뜁니다 |
578
+
579
+ 날짜, 버전, 각 검증이 찾은 것은 [docs/VERIFICATION.md](https://github.com/LeeSongHeon-LSH/lshed/blob/main/docs/VERIFICATION.md)에 있습니다. 거기 있는 것은 전부 한 사람의 기기입니다. 여기 없는 도구 버전이나 OS 에서 lshed 가 잘 돌았다면 [검증 보고](https://github.com/LeeSongHeon-LSH/lshed/issues/new?template=verified.yml)를 남겨 주세요(2분이면 됩니다). 안 돌았다면 `lshed report` 가 [버그 보고](https://github.com/LeeSongHeon-LSH/lshed/issues/new?template=bug.yml)에 필요한 것을 값이나 시크릿 없이, 홈은 `~` 로 가려 찍어 줍니다.
580
+
581
+ ## 아직 범위 밖
582
+
583
+ - "변수 이름만 적는" 것 이상의 시크릿 처리. 암호화된 값, `op://` 참조, OS 키체인은 나중 일이고, 지금은 일부러 dotfiles보다 나을 것이 없게 두었습니다.
584
+ - 프로젝트 범위 MCP 서버(`.mcp.json`, `~/.claude.json`의 `projects.*`)와 프로젝트 범위 플러그인. 프로젝트의 것입니다.
585
+ - 다른 에이전트는 스킬, 지침 파일, MCP 서버만 옮깁니다. 각 도구의 설정 파일, 규칙 폴더, 플러그인은 그대로 둡니다.
577
586
 
578
587
  ## 라이선스
579
588