hypomnema 1.7.3 → 1.7.4

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.
@@ -11,7 +11,7 @@
11
11
  "name": "hypo",
12
12
  "source": "./",
13
13
  "description": "LLM-native personal wiki — session-aware knowledge base for Claude Code",
14
- "version": "1.7.3",
14
+ "version": "1.7.4",
15
15
  "homepage": "https://github.com/sk-lim19f/Hypomnema"
16
16
  }
17
17
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypo",
3
- "version": "1.7.3",
3
+ "version": "1.7.4",
4
4
  "description": "LLM-native personal wiki system — session-aware knowledge base for Claude Code",
5
5
  "author": {
6
6
  "name": "sk-lim19f",
package/README.ko.md CHANGED
@@ -74,9 +74,9 @@ hypomnema
74
74
 
75
75
  > 어느 경로든 첫 실행 뒤에는 Claude Code를 재시작(또는 새 세션 열기)해야 새 훅과 슬래시 커맨드가 반영됩니다.
76
76
 
77
- > `init`은 `~/.zshrc`(또는 `~/.bashrc`)에 `claude()` 셸 함수도 추가합니다. `# hypo-managed:shell-setup:start`와 `:end` 마커 사이에 들어가며, `cd`만 한 세션에도 프로젝트 컨텍스트가 주입되게 합니다. `--no-shell`로 건너뛰거나 `--shell-config=<path>`로 대상 파일을 바꿀 수 있습니다. `uninstall`은 이 블록을 지우지 않으니 마커 사이 줄을 직접 지우세요.
77
+ > `init`은 `~/.zshrc`(또는 `~/.bashrc`)에 `claude()` 셸 함수도 추가합니다. `# hypo-managed:shell-setup:start`와 `:end` 마커 사이에 들어가며, `cd`만 한 세션에도 프로젝트 컨텍스트가 주입되게 합니다. `--no-shell`로 건너뛰거나 `--shell-config=<path>`로 대상 파일을 바꿀 수 있습니다. `uninstall --apply`는 이 블록을 지웁니다(기본으로 `~/.zshrc`와 `~/.bashrc` 확인하고, `--shell-config`로 지정한 파일이 있으면 그것만 봅니다). 다만 마커를 손으로 편집해서 중복되거나 순서가 뒤바뀐 상태라면, 무엇을 지워야 할지 안전하게 판단할 수 없으므로 블록을 그대로 둡니다. `--keep-shell`을 주면 이 셸 rc 정리만 건너뛰고 나머지 uninstall 은 그대로 돕니다.
78
78
 
79
- > `init`은 위키 저장소에 pre-commit 훅도 설치합니다(`<위키>/.git/hooks/pre-commit`, `# hypo-managed:pre-commit:*` 마커). `.hypoignore`에 걸리는 파일이 스테이지되면 커밋을 막습니다. 해당 파일을 unstage 하거나 `git commit --no-verify`로 우회하세요. 이 훅에는 설치된 패키지의 절대 경로가 박히므로, Hypomnema를 옮기거나 재설치했다면 `hypomnema init`을 다시 돌려 경로를 갱신해야 합니다. 그러지 않으면 위키의 모든 커밋이 실패합니다. `uninstall`은파일을 지우지 않습니다.
79
+ > `init`은 위키 저장소에 pre-commit 훅도 설치합니다(`<위키>/.git/hooks/pre-commit`, `# hypo-managed:pre-commit:*` 마커). `.hypoignore`에 걸리는 파일이 스테이지되면 커밋을 막습니다. 해당 파일을 unstage 하거나 `git commit --no-verify`로 우회하세요. 이 훅에는 설치된 패키지의 절대 경로가 박히므로, Hypomnema를 옮기거나 재설치했다면 `hypomnema init`을 다시 돌려 경로를 갱신해야 합니다. 그러지 않으면 위키의 모든 커밋이 실패합니다. `uninstall --apply`는 훅을 지웁니다. 위키 위치는 `init`과 같은 방식으로 찾고, `--hypo-dir=<path>`로 직접 지정할 수도 있습니다. 다만 마커는 있지만 그 바깥에 다른 내용이 붙은 훅처럼 더 이상 온전히 Hypomnema 소유가 아닌 훅은 손대지 않고 그대로 둡니다. `--keep-wiki-hook`을 주면 이 위키 훅 정리만 건너뛰고 나머지 uninstall 은 그대로 돕니다.
80
80
 
81
81
  > 두 번째 기기에서는 그냥 `hypomnema`를 돌리지 마세요. 원격과 무관한 새 위키가 만들어집니다. 기존 위키를 클론하는 쪽을 쓰세요. `hypomnema --from-remote=<git-url>`이 위키 루트로 클론한 뒤 실제 Hypomnema 위키인지 확인하고, 새 git 히스토리를 만들지 않은 채 훅과 슬래시 커맨드만 설치합니다. 대상 디렉터리가 이미 있으면 거부합니다.
82
82
 
@@ -404,7 +404,7 @@ E. 멈춘 프로젝트 재개.
404
404
 
405
405
  | 설치 경로 | 슬래시 커맨드 위치 |
406
406
  |---|---|
407
- | 플러그인 (Path A) | Claude Code 플러그인 캐시. `/plugin marketplace update hypomnema` 후 `/reload-plugins`로 갱신 |
407
+ | 플러그인 (Path A) | Claude Code 플러그인 캐시. `/plugin marketplace update hypomnema` 후 `/reload-plugins`로 갱신. 캐시 디렉터리 이름이 매니페스트 버전이라, 그 버전이 바뀌어야 갱신이 반영된다. 버전을 그대로 둔 채 머지한 커밋은 이미 설치된 곳에 닿지 않는다. |
408
408
  | npm CLI (Path B) | `~/.claude/commands/hypo/`. `hypomnema upgrade --apply`로 갱신, 파일별 SHA 추적. 사용자 수정본까지 덮어쓰려면 `--force-commands`(원본은 `.bak`으로 보존) |
409
409
 
410
410
  ---
package/README.md CHANGED
@@ -72,7 +72,7 @@ hypomnema
72
72
 
73
73
  `hypomnema` (or `hypomnema --help` for flags) scaffolds the wiki and installs hooks. It also copies the slash command files to `~/.claude/commands/hypo/`, so `/hypo:*` works inside Claude Code afterwards. Later `hypomnema upgrade` runs use per-file SHA tracking, so anything you hand-edited stays put.
74
74
 
75
- `init` also appends a `claude()` shell function to `~/.zshrc` or `~/.bashrc`, marked between `# hypo-managed:shell-setup:start` and `:end`, so a cd-only session still gets project context. Pass `--no-shell` to skip it, or `--shell-config=<path>` to target a different file. `uninstall` does not remove this block; delete the marked lines by hand.
75
+ `init` also appends a `claude()` shell function to `~/.zshrc` or `~/.bashrc`, marked between `# hypo-managed:shell-setup:start` and `:end`, so a cd-only session still gets project context. Pass `--no-shell` to skip it, or `--shell-config=<path>` to target a different file. `uninstall --apply` removes this block (checking both `~/.zshrc` and `~/.bashrc` by default, or the file named by `--shell-config`); it leaves the block untouched if a hand-edit has since duplicated or reordered the markers, since it has no safe way to guess what you meant. Pass `--keep-shell` to skip this shell rc cleanup (the rest of the uninstall still runs).
76
76
 
77
77
  > Either path: restart Claude Code (or open a new session) after the first run so the new hooks and slash commands are picked up.
78
78
 
@@ -384,7 +384,7 @@ Place a `hypo-config.md` at the wiki root to make it portable across machines wi
384
384
 
385
385
  `.hypoignore` controls which paths the hooks ignore (default: `*.pdf`, `*.zip`, `*.pem`, `.env*`, `*credentials*`, `*secret*`, `*token*`, `*password*`, `*passwd*`, …). Edit it directly; there is no privacy mode flag. One file, one source of truth.
386
386
 
387
- > `init` also installs a pre-commit hook into the wiki repo at `<wiki>/.git/hooks/pre-commit`, marked `# hypo-managed:pre-commit:*`. It refuses any commit that stages a path matching `.hypoignore`; unstage the file, or override with `git commit --no-verify`. The hook embeds the absolute path of the installed package, so if you move or reinstall Hypomnema, re-run `hypomnema init` to repoint it or every wiki commit will fail. `uninstall` leaves this file in place.
387
+ > `init` also installs a pre-commit hook into the wiki repo at `<wiki>/.git/hooks/pre-commit`, marked `# hypo-managed:pre-commit:*`. It refuses any commit that stages a path matching `.hypoignore`; unstage the file, or override with `git commit --no-verify`. The hook embeds the absolute path of the installed package, so if you move or reinstall Hypomnema, re-run `hypomnema init` to repoint it or every wiki commit will fail. `uninstall --apply` removes this hook, resolving the vault the same way `init` does (`--hypo-dir=<path>` to override); it only removes a hook that still carries the marker and no content outside it, so a hook you have since edited by hand is left in place. Pass `--keep-wiki-hook` to skip this wiki hook cleanup (the rest of the uninstall still runs).
388
388
 
389
389
  > The credential-style patterns above are substring globs, so an ordinary page such as `pages/oauth-token-refresh.md` is matched too: it is never injected by any hook, and the wiki pre-commit hook refuses to commit it. If you write about these topics, narrow the patterns or rename the page.
390
390
 
@@ -404,7 +404,7 @@ Place a `hypo-config.md` at the wiki root to make it portable across machines wi
404
404
 
405
405
  | Install path | Slash commands served from |
406
406
  |---|---|
407
- | Plugin (Path A) | Claude Code's plugin cache; updated via `/plugin marketplace update hypomnema` then `/reload-plugins` |
407
+ | Plugin (Path A) | Claude Code's plugin cache; updated via `/plugin marketplace update hypomnema` then `/reload-plugins`. The cache directory is named after the manifest version, so an update lands only when that version has changed. A commit merged under an unchanged version does not reach an existing install. |
408
408
  | npm CLI (Path B) | `~/.claude/commands/hypo/`; updated via `hypomnema upgrade --apply` with per-file SHA tracking. Pass `--force-commands` to overwrite hand-edits (creates `.bak`). |
409
409
 
410
410
  ---
@@ -6,7 +6,7 @@ You are running `/hypo:capture`. Bring a command, agent, or skill that the user
6
6
 
7
7
  Scope: commands, agents, skills, and hooks. Commands and agents are enumerated from `~/.claude/{commands,agents}/`; a skill is a whole directory (`~/.claude/skills/<name>/SKILL.md` plus its subtree); hooks are read from the `~/.claude/settings.json` registration.
8
8
 
9
- Hooks and skills are captured only when they round-trip losslessly, because what lands in the wiki is exactly what the far machine installs. A hook qualifies when its command is the canonical `node $HOME/.claude/hooks/<name>.mjs` form and its event, matcher, and timeout are preserved. A skill is refused whole (never captured as a partial subset) when its subtree holds anything that cannot survive the trip: a symlink, a hardlink, an empty directory (git cannot carry one), a VCS control directory, or more than 500 files / 5 MiB. That ceiling is what keeps a vendored skill with its own `node_modules` out of the vault. Content is reproduced byte for byte; the executable bit is not carried by sync yet, so a captured skill holding executable scripts prints a warning.
9
+ Hooks and skills are captured only when they round-trip losslessly, because what lands in the wiki is exactly what the far machine installs. A hook qualifies when its command is the canonical `node $HOME/.claude/hooks/<name>.mjs` form and its event, matcher, and timeout are preserved. A skill is refused whole (never captured as a partial subset) when its subtree holds anything that cannot survive the trip: a symlink, a hardlink, an empty directory (git cannot carry one), a VCS control directory, or more than 500 files / 5 MiB. That ceiling is what keeps a vendored skill with its own `node_modules` out of the vault. Content is reproduced byte for byte, and a newly captured executable script keeps its executable bit through the wiki and onto every install. A skill captured before this behavior shipped has its wiki copy recorded non-executable, and there is no automatic fix once that has happened: it is already managed by the wiki, so it never shows up as a capture candidate again, and forward-sync's own heal only adds a missing exec bit while the install's content still matches the wiki record byte for byte. If the wiki content changes later for an unrelated reason, the install picks up whatever mode the new wiki bytes carry, exec bit included, so the mode can move in either direction at that point, not just up. The only working fix for an already-committed non-executable script is `chmod +x` directly on the wiki file; since that is a mode-only change with the content untouched, forward-sync's heal then carries the bit to every install from there.
10
10
 
11
11
  ## Step 1: Resolve the package root
12
12
 
@@ -20,8 +20,8 @@ If `/hypo:crystallize` was invoked to close a session (via an explicit close sig
20
20
  Before composing the payload (Step 2), run these four reflections and surface each to the user. Every one is **advisory** (identity guard): the user confirms or declines, and none performs an automatic action, writes a file on its own, or bypasses the mandatory gate.
21
21
 
22
22
  1. **Trivial-session check (#44)** — Was this session trivial (a single bug fix, a single-file edit, or Q&A with no durable artifact)? If so, recommend skipping session-close: *"이 세션은 trivial해 보입니다 — session-close를 건너뛸까요?"* and proceed only if the user wants a close. A trivial skip is a recommendation, **not** a bypass: it must not mark the session closed, must not run `--mark-session-closed`, and must not claim `/compact` can pass. Any real close still requires all 5 mandatory files.
23
- 2. **ADR-candidate check (#41)** Did this session make an architectural or design decision (a new pattern, a tradeoff chosen, a convention established)? If yes, ask whether it warrants an ADR and, if so, capture that intent in the `sessionLog` entry you compose in Step 2. If nothing rose to ADR level, you may record `ADR 없음 <one-line reason>` in that same `sessionLog` entry but gate it on #42's bar: the marker is machine-read and W8 excludes an entry carrying `ADR 없음` (with no ADR reference) from the design-history staleness check. Write it only when the session had **no design change at all**; a sub-ADR design shift takes #42a (append) instead, since the marker would suppress the W8 nudge it needs. **Never auto-write an ADR file** recording the decision (or its absence) in the session-log payload is the only action here.
24
- 3. **design-history staleness check (#42)** Two branches, so a stale W8 never blocks a clean close: (a) if this session changed design decisions `projects/<name>/design-history.md` does not yet reflect including sub-ADR background / tradeoff / differentiation shifts recommend appending now (the W8 lint warning flags this mechanically; an active-project W8 hard-blocks at PreCompact append before you commit). (b) only if the session made **no** design change does the `ADR 없음` marker (#41) exempt the entry from W8; do not touch design-history. `ADR 없음` means "no design change," a stricter bar than "no ADR-level decision." If the file does not exist, skip silently — do **not** create it just for this check. Never auto-update it.
23
+ 2. **ADR-candidate check (#41).** Did this session make an architectural or design decision (a new pattern, a tradeoff chosen, a convention established)? If yes, ask whether it warrants an ADR and, if so, capture that intent in the `sessionLog` entry you compose in Step 2. If nothing rose to ADR level, you may record `ADR 없음: <one-line reason>` in that same `sessionLog` entry, but gate it on #42's bar: the marker is machine-read and W8 excludes an entry carrying `ADR 없음` (with no ADR reference) from the design-history staleness check. Write it only when the session had **no design change at all**; a sub-ADR design shift takes #42a (append) instead, since the marker would suppress the W8 nudge it needs. **Never auto-write an ADR file.** Recording the decision (or its absence) in the session-log payload is the only action here. This check carries no `decisions/` directory precondition: run it whether or not that directory exists.
24
+ 3. **design-history staleness check (#42).** Two branches, so a stale W8 never blocks a clean close: (a) if this session changed design decisions `projects/<name>/design-history.md` does not yet reflect (including sub-ADR background, tradeoff, or differentiation shifts), recommend appending now: the W8 lint warning flags this mechanically, and an active-project W8 hard-blocks at PreCompact, so append before you commit. **If the file does not exist yet and this session had a design change, recommend creating it now** with that change as the first entry; lint separately flags a missing-but-needed file as W14, a warning that never blocks. (b) only if the session made **no** design change does the `ADR 없음` marker (#41) exempt the entry from W8; do not touch design-history, and do not create the file just to satisfy this branch's check. `ADR 없음` means "no design change," a stricter bar than "no ADR-level decision." Never auto-write the file yourself in either branch: recommend it, and let the user decide.
25
25
  4. **Ingest check (#43)** — Did this session consume trustworthy external knowledge (a fetched URL, official docs, or code you verified directly)? If so, recommend running `/hypo:ingest` to capture it under `sources/`. Proceed only on the user's confirmation.
26
26
 
27
27
  These are judgment calls; when uncertain, surface the question rather than skip it. None of the four blocks the close or writes on its own.
@@ -8,14 +8,19 @@ You are running `/hypo:uninstall`. Remove Hypomnema from this machine.
8
8
 
9
9
  - Removes Hypomnema hook files from `~/.claude/hooks/` (and optionally `~/.codex/hooks/`)
10
10
  - Strips Hypomnema entries from `~/.claude/settings.json`, leaving all other hooks untouched
11
- - **Dry-run by default** shows what would be removed without making any changes
11
+ - Removes the `claude()` shell function block from `~/.zshrc` and/or `~/.bashrc` (whichever carries the marker `init` wrote; use `--shell-config=<path>` to target a different file)
12
+ - Removes the marked pre-commit hook from the wiki repo (`<wiki>/.git/hooks/pre-commit`); resolve a non-default wiki with `--hypo-dir=<path>`
13
+ - Removes tracked slash commands, extension hard-copies, and `~/.claude/hypo-pkg.json` when nothing user-modified is left behind
14
+ - Every removal above is marker- or SHA-gated: a block or file you have since hand-edited is reported and left in place, never guessed at
15
+ - **The wiki content itself (pages, journal, sources under the vault root) is never deleted, only the git hook and shell block Hypomnema installed**
16
+ - **Dry-run by default**: shows what would be removed without making any changes
12
17
 
13
18
  ---
14
19
 
15
20
  ## Step 1 — Confirm intent
16
21
 
17
22
  Say:
18
- > "This will remove Hypomnema hooks from your system. Your wiki files are NOT deleted.
23
+ > "This will remove Hypomnema's hooks, slash commands, the claude() shell function block, and the wiki's pre-commit hook. Your wiki content (pages, journal, sources) is NOT deleted.
19
24
  > Run in dry-run mode first to preview changes? [yes]"
20
25
 
21
26
  Default: yes (dry-run first)
@@ -44,11 +49,18 @@ If no → abort and confirm nothing was changed.
44
49
  node ${CLAUDE_PLUGIN_ROOT}/scripts/uninstall.mjs --apply
45
50
  ```
46
51
 
47
- If the user also wants Codex hooks removed, append `--codex`.
52
+ If the user also wants Codex hooks removed, append `--codex`. Other flags worth knowing about:
53
+ - `--hypo-dir=<path>`: the wiki whose pre-commit hook gets removed, if it is not the default-resolved one
54
+ - `--shell-config=<path>`: the single rc file to strip the shell block from, instead of checking both `~/.zshrc` and `~/.bashrc`
55
+ - `--force-commands` / `--force-extensions`: remove a slash command or extension file even if its content no longer matches what Hypomnema installed
56
+ - `--hooks-dir=<path>`: only redirects the `~/.claude/hooks/*.mjs` cleanup, so a run scoped to a sandbox hooks directory still touches the real shell rc files and wiki vault unless `--keep-shell` and/or `--keep-wiki-hook` are also passed. The script warns about this before it does anything if it detects the combination
57
+ - `--keep-shell`: skip the shell rc `claude()` block removal entirely
58
+ - `--keep-wiki-hook`: skip the wiki pre-commit hook removal entirely, including the step that resolves which vault it would have looked at
48
59
 
49
60
  ---
50
61
 
51
62
  ## Notes
52
63
 
53
- - Wiki content (`~/hypomnema/`) is never touched — only hook files and settings.json entries
64
+ - Wiki content under `~/hypomnema/` (pages, journal, sources, etc.) is never touched
65
+ - What IS removed by `--apply`: hook files, settings.json entries, the shell rc `claude()` block, and the wiki's own pre-commit hook
54
66
  - To reinstall, run `/hypo:init`
@@ -233,7 +233,7 @@ Hooks inline this logic in `hypo-shared.mjs`. Scripts use `scripts/lib/hypo-root
233
233
 
234
234
  ### `/hypo:uninstall`
235
235
 
236
- Removes hypo-prefixed hooks from `~/.claude/hooks/` and matching entries from `~/.claude/settings.json`. **Non-hypo hooks are preserved**. The wiki vault itself is never touched.
236
+ Removes hypo-prefixed hooks from `~/.claude/hooks/` and matching entries from `~/.claude/settings.json`. **Non-hypo hooks are preserved**. `--apply` also reaches past `~/.claude/`: it strips the marked `claude()` block from the shell rc file(s) init wrote to, and removes the marked pre-commit hook from the wiki's own git repo. Both removals are marker-gated the same way the hooks-dir cleanup is, so a hand-edited block or hook is left in place rather than guessed at.
237
237
 
238
238
  ---
239
239
 
@@ -120,7 +120,8 @@ If you need to share new logic, prefer extending an existing helper over adding
120
120
 
121
121
  ```bash
122
122
  npm test # tests/*.test.mjs, sharded across processes — unit + smoke + contract
123
- npm run lint # scripts/lint.mjs — frontmatter + wikilink validation + W8 (design-history stale vs session-log)
123
+ npm run lint # scripts/lint.mjs — frontmatter + wikilink validation + W8 (design-history stale
124
+ # vs session-log) + W14 (design-history missing but session-log implies one)
124
125
  npm run fix:verify # Phase 1 of learned_behavior #6 — verifies fix #N status claims in
125
126
  # a wiki spec against `// @fix #N: <test-name>` anchors, read as a
126
127
  # union across every tests/*.mjs. Maintainer dogfood; needs a wiki at
@@ -172,17 +173,19 @@ Some hook behavior is only observable inside a Claude Code session. Document the
172
173
 
173
174
  `npm install` in this checkout installs a git `pre-commit` hook that runs `prettier --write` on staged files only. The hook is **non-blocking**: formatter failures print a notice but the commit still proceeds. The only block is when `git add` itself fails during restage (true index corruption).
174
175
 
175
- **Requirements**: Git ≥ 2.13 (uses `--absolute-git-dir`; `--git-common-dir` is 2.5+).
176
+ **Requirements**: Git ≥ 2.13. The installed shell shim itself only calls `--git-common-dir` (2.5+), but the installer and `pre-commit-format.mjs`'s own identity guard still call `--absolute-git-dir`, which is what actually sets the floor.
176
177
 
177
178
  **Path-locked to your checkout.** The shim embeds the absolute paths of your `HYPOMNEMA_ROOT` and `.git/` directory at install time. If you `mv` the checkout, re-run `npm install` to regenerate the shim — until then it safely no-ops.
178
179
 
179
- **Main worktree only.** `git worktree add` checkouts silently skip the shared `.git/hooks/pre-commit` can only point at one embedded root at a time. Commit from the main worktree to get auto-format, or accept the no-op in linked worktrees.
180
+ **Linked worktrees work too.** Both the shell shim and `pre-commit-format.mjs`'s own identity guard compare on `--git-common-dir`, not `--show-toplevel` or `--absolute-git-dir`. A linked worktree's toplevel and git dir differ from the main checkout's, but its common dir is still the same shared `.git`, so a commit made from a linked worktree gets the same auto-format and tracker-id gate as one made from the main checkout.
180
181
 
181
182
  **CI is skipped.** `npm ci` runs `prepare`, but the installer detects `CI=true` and exits 0 without touching `.git/hooks/`. CI runs never mutate hooks.
182
183
 
183
184
  **Symlink-safe.** If `.git/hooks/` is a symlink, or an existing `pre-commit` is a symlink, the installer refuses to write through it.
184
185
 
185
- **Shared `core.hooksPath` safe.** The shim verifies both `--show-toplevel` and `--absolute-git-dir` against the embedded values before executing. Foreign repos that share your global `core.hooksPath` will silently no-op.
186
+ **Shared `core.hooksPath` safe.** The shim verifies the live `--git-common-dir` against the embedded value before executing; `pre-commit-format.mjs` re-checks the same axis plus containment on `--absolute-git-dir` as a second, independent layer. Foreign repos that share your global `core.hooksPath` have their own, different common dir, so they still silently no-op.
187
+
188
+ **Reversed `GIT_DIR`/`GIT_WORK_TREE` mixes are also closed.** The common-dir check above proves the git dir Git reports is ours; it never looks at `--show-toplevel`. That leaves a reversed mix open: `GIT_DIR` pointed at one of your own linked-worktree admin dirs (genuinely ours) while `GIT_WORK_TREE` points at an unrelated repo. `pre-commit-format.mjs` closes it with a worktree-binding check: it reads the toplevel's own `.git` entry (a directory for a main checkout, a `gitdir:` pointer file for a linked worktree) and requires it to resolve back to the git dir Git reported. A foreign toplevel's `.git` always points at its own git dir, never at yours, so it fails there regardless of what `GIT_DIR`/`GIT_WORK_TREE` claim. The shell shim's own `--git-common-dir` check is a fast pre-filter only; this second axis is enforced in the Node script, not the shell.
186
189
 
187
190
  **Env-override defense.** The Node side strips every `GIT_*` env from `git rev-parse --local-env-vars` (plus `GIT_NAMESPACE`, `GIT_CEILING_DIRECTORIES`, `GIT_CONFIG_*`) before its own git spawns. Inherited `GIT_INDEX_FILE` is preserved **only** when invoked from the installed shell shim (signalled via a sentinel env var). Direct `node scripts/pre-commit-format.mjs` invocation drops `GIT_INDEX_FILE` and falls back to the default `.git/index`, closing the class of attacks that try to drive the formatter against a crafted alternate index.
188
191
 
@@ -326,6 +329,12 @@ in **both** the CHANGELOG section AND the git tag annotation. The release
326
329
  workflow enforces this with `scripts/check-bilingual.mjs`; a lightweight tag, or
327
330
  a gated CHANGELOG section missing its `#### 한국어` sub-block, will block `npm publish`.
328
331
 
332
+ A release is what makes a fix reachable, on both channels. The plugin installer names its
333
+ cache directory after the manifest version, so it skips the copy when that version has not
334
+ moved: commits merged to `main` under an unchanged version never reach an existing install.
335
+ "It is on main, so people have it" is false. Bump the version and cut the release, or the
336
+ work sits where nobody can run it.
337
+
329
338
  The READMEs are not part of a release. They describe what Hypomnema does now, not
330
339
  what each version added, so cutting a release never edits them. Version history
331
340
  lives in `CHANGELOG.md` alone. (A gate used to require the release version to