hypomnema 1.3.1 → 1.3.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.
Files changed (46) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/README.ko.md +10 -10
  4. package/README.md +10 -10
  5. package/commands/crystallize.md +8 -8
  6. package/commands/feedback.md +1 -1
  7. package/commands/resume.md +1 -1
  8. package/docs/ARCHITECTURE.md +3 -3
  9. package/docs/CONTRIBUTING.md +2 -2
  10. package/hooks/hypo-auto-minimal-crystallize.mjs +57 -11
  11. package/hooks/hypo-compact-guard.mjs +1 -1
  12. package/hooks/hypo-cwd-change.mjs +1 -1
  13. package/hooks/hypo-first-prompt.mjs +2 -2
  14. package/hooks/hypo-hot-rebuild.mjs +14 -1
  15. package/hooks/hypo-personal-check.mjs +91 -179
  16. package/hooks/hypo-pre-commit.mjs +1 -1
  17. package/hooks/hypo-session-end.mjs +1 -1
  18. package/hooks/hypo-session-start.mjs +7 -7
  19. package/hooks/hypo-shared.mjs +788 -72
  20. package/hooks/hypo-web-fetch-ingest.mjs +2 -2
  21. package/hooks/version-check-fetch.mjs +2 -8
  22. package/hooks/version-check.mjs +18 -0
  23. package/package.json +3 -2
  24. package/scripts/check-tracker-ids.mjs +329 -0
  25. package/scripts/crystallize.mjs +244 -109
  26. package/scripts/doctor.mjs +6 -9
  27. package/scripts/feedback-sync.mjs +1 -1
  28. package/scripts/init.mjs +1 -1
  29. package/scripts/install-git-hooks.mjs +75 -40
  30. package/scripts/lib/check-tracker-ids.mjs +140 -0
  31. package/scripts/lib/design-history-stale.mjs +59 -14
  32. package/scripts/lib/extensions.mjs +4 -4
  33. package/scripts/lib/fix-manifest.mjs +2 -2
  34. package/scripts/lib/fix-status-verify.mjs +5 -4
  35. package/scripts/lib/plugin-detect.mjs +15 -6
  36. package/scripts/lib/project-create.mjs +1 -1
  37. package/scripts/lint.mjs +63 -8
  38. package/scripts/rename.mjs +373 -0
  39. package/scripts/resume.mjs +75 -19
  40. package/scripts/uninstall.mjs +1 -1
  41. package/scripts/upgrade.mjs +26 -25
  42. package/skills/crystallize/SKILL.md +10 -7
  43. package/templates/SCHEMA.md +2 -2
  44. package/templates/hypo-config.md +2 -2
  45. package/templates/hypo-guide.md +20 -1
  46. package/templates/projects/_template/index.md +1 -1
@@ -8,10 +8,10 @@
8
8
  },
9
9
  "plugins": [
10
10
  {
11
- "name": "hypomnema",
11
+ "name": "hypo",
12
12
  "source": "./",
13
13
  "description": "LLM-native personal wiki — session-aware knowledge base for Claude Code",
14
- "version": "1.3.1",
14
+ "version": "1.3.2",
15
15
  "homepage": "https://github.com/sk-lim19f/Hypomnema"
16
16
  }
17
17
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "hypomnema",
3
- "version": "1.3.1",
2
+ "name": "hypo",
3
+ "version": "1.3.2",
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
@@ -24,14 +24,14 @@ _Claude에게 기록을 맡기세요 — 그리고 그 기록이 실제로 쌓
24
24
  > **아래에서 자주 쓰이는 용어 간단 정리.** *프런트매터(frontmatter)* = 마크다운 파일 맨 위의 YAML 블록. *위키링크(wikilink)* = `[[페이지-슬러그]]` 형태의 교차 참조. *ADR* = "Architecture Decision Record" — 어떤 설계 결정을 *왜* 했는지 짧게 적은 마크다운 페이지. *projection*(투영) = 한 방향 자동 파생(`pages/feedback/*.md` → `MEMORY.md` / `<learned_behaviors>`). *훅(hook)* = Claude Code가 라이프사이클 이벤트에서 자동으로 실행하는 스크립트. *hot.md* / *session-state.md* = "방금 무엇을 했는지"와 "다음에 무엇을 할지"를 담는 프로젝트별 캐시 파일 — 멈춘 프로젝트를 한 번에 이어 받을 수 있게 합니다. 전체 용어 풀이는 [용어 사전](#용어-사전) 참조.
25
25
 
26
26
  > **현재 자동화 범위와 다음 목표.** v1.3.0(현재)은 트리거 모델을 솔직하게 정리합니다. 위키 작업(자료 정리·검색·세션 마무리)은 여전히 사용자가 `/hypo:*` 명령어를 직접 입력해 시작합니다. 다만 **v1.1.0**부터 위키가 한 세션에서 얼마나 활용됐는지를 측정하는 *관측성 지표(observability score)* 가 들어갔고, **v1.2.0**은 그 위에 사용자가 시키지 않아도 자동으로 동작하는 영역 4개를 추가했습니다:
27
- > - **`feedback` 페이지를 단일 원천(source of truth)으로**(ADR 0031) — `pages/feedback/`에 한 번만 적으면, 위키가 `MEMORY.md`와 `~/.claude/CLAUDE.md`의 `<learned_behaviors>` 블록을 자동으로 갱신합니다.
28
- > - **확장 파일 동봉 동기화**(ADR 0024) — 위키 안의 `~/hypomnema/extensions/{agents,commands,hooks,skills}/`에 둔 파일을 자동으로 `~/.claude/`에 반영합니다. `--codex` 옵션을 추가하면 `hooks`·`commands`는 `~/.codex/`에도 반영되지만, `agents`와 `skills`는 Claude 전용이라 의도적으로 건너뜁니다.
29
- > - **프로젝트 자동 생성**(ADR 0023) — 작업 디렉터리를 git 저장소(`package.json`·`Cargo.toml` 등의 프로젝트 표식이 있는 곳)로 옮겼을 때 대응하는 위키 프로젝트가 없으면, 새로 만들지 물어봅니다.
30
- > - **세션 종료 자동 정리와 `/clear` 복구**(ADR 0022) — 의미 있는 세션이 끝날 때 "마무리 메모를 짧게 남길까요?"가 자동으로 뜨고, 마무리하지 않은 채 `/clear`를 입력해도 다음 세션 시작 시 이어서 정리할 수 있습니다.
27
+ > - **`feedback` 페이지를 단일 원천(source of truth)으로** — `pages/feedback/`에 한 번만 적으면, 위키가 `MEMORY.md`와 `~/.claude/CLAUDE.md`의 `<learned_behaviors>` 블록을 자동으로 갱신합니다.
28
+ > - **확장 파일 동봉 동기화** — 위키 안의 `~/hypomnema/extensions/{agents,commands,hooks,skills}/`에 둔 파일을 자동으로 `~/.claude/`에 반영합니다. `--codex` 옵션을 추가하면 `hooks`·`commands`는 `~/.codex/`에도 반영되지만, `agents`와 `skills`는 Claude 전용이라 의도적으로 건너뜁니다.
29
+ > - **프로젝트 자동 생성** — 작업 디렉터리를 git 저장소(`package.json`·`Cargo.toml` 등의 프로젝트 표식이 있는 곳)로 옮겼을 때 대응하는 위키 프로젝트가 없으면, 새로 만들지 물어봅니다.
30
+ > - **세션 종료 자동 정리와 `/clear` 복구** — 의미 있는 세션이 끝날 때 "마무리 메모를 짧게 남길까요?"가 자동으로 뜨고, 마무리하지 않은 채 `/clear`를 입력해도 다음 세션 시작 시 이어서 정리할 수 있습니다.
31
31
  >
32
32
  > 스키마(`SCHEMA.md`)는 2.0으로 올라갑니다. `feedback` 페이지 타입에 9개의 필수 항목이 추가되며, `hypomnema upgrade --apply`를 실행하면 위키 루트에 `MIGRATION-v2.0.md`가 생성되어 단계별 보강 체크리스트를 제공합니다. 사용자가 직접 편집한 `SCHEMA.md`는 upgrade가 **덮어쓰지 않습니다** — 안내만 표시하고, 실제 반영은 사용자가 수동으로 결정합니다(이 정책을 코드에서는 *Option C*로 부릅니다).
33
33
  >
34
- > **v1.3.0**은 자율성을 넓히기보다 이 레이어를 다듬습니다. 세션 마무리 흐름에 *권고형* 성찰 4가지(자동 실행 없이 제안만 — ADR 0029)가 들어가고, `hypomnema lint --strict`가 선택된 경고를 에러로 승격해 릴리스 게이트로 쓸 수 있으며, 설치가 **stale-sibling 감지**로 단단해집니다 — `$PATH`에 남은 더 오래된 `hypomnema`가 더는 활성 훅을 조용히 다운그레이드하지 못합니다(ADR 0038).
34
+ > **v1.3.0**은 자율성을 넓히기보다 이 레이어를 다듬습니다. 세션 마무리 흐름에 *권고형* 성찰 4가지(자동 실행 없이 제안만)가 들어가고, `hypomnema lint --strict`가 선택된 경고를 에러로 승격해 릴리스 게이트로 쓸 수 있으며, 설치가 **stale-sibling 감지**로 단단해집니다 — `$PATH`에 남은 더 오래된 `hypomnema`가 더는 활성 훅을 조용히 다운그레이드하지 못합니다.
35
35
 
36
36
  ---
37
37
 
@@ -45,7 +45,7 @@ Claude Code 안에서:
45
45
 
46
46
  ```
47
47
  /plugin marketplace add sk-lim19f/Hypomnema
48
- /plugin install hypomnema@hypomnema
48
+ /plugin install hypo@hypomnema
49
49
  /hypo:init
50
50
  ```
51
51
 
@@ -240,16 +240,16 @@ v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실
240
240
  | `hypo-auto-commit.mjs` | `Stop` | 자동 commit + pull + push |
241
241
  | `hypo-hot-rebuild.mjs` | `Stop` | `hot.md` 재생성 |
242
242
  | `hypo-personal-check.mjs` | `PreCompact` | lint 실패 / session-close 미완 시 compact 차단 |
243
- | `hypo-session-end.mjs` | `SessionEnd` | SessionEnd 마커 기록 — 다음 SessionStart가 `source=clear` 복구를 감지하기 위함 (ADR 0022) |
243
+ | `hypo-session-end.mjs` | `SessionEnd` | SessionEnd 마커 기록 — 다음 SessionStart가 `source=clear` 복구를 감지하기 위함 |
244
244
  | `hypo-session-record.mjs` | `Stop` | observability 점수 + auto-resume 신호용 세션 메타데이터 기록 |
245
- | `hypo-auto-minimal-crystallize.mjs` | `Stop` | 단순하지 않은 세션이 끝났을 때 `/hypo:crystallize --apply-session-close --minimal`을 자동 제안 (사용자가 동의하면 실행, ADR 0022 Layer 3) |
245
+ | `hypo-auto-minimal-crystallize.mjs` | `Stop` | 단순하지 않은 세션이 끝났을 때 `/hypo:crystallize --apply-session-close --minimal`을 자동 제안 (사용자가 동의하면 실행) |
246
246
  | `hypo-web-fetch-ingest.mjs` | `PostToolUse(WebFetch/WebSearch)` | WebFetch/WebSearch 완료 후 `additionalContext`에 `/hypo:ingest` 권유 안내 주입 (privacy 보호: WebFetch URL의 query/hash/userinfo 제거) |
247
247
 
248
248
  모든 훅은 위키 루트를 `HYPO_DIR` 환경변수 → `hypo-config.md` 스캔 → `~/hypomnema` 기본값 순으로 해결하며, `hypo-shared.mjs`(`hooks.json`의 `shared` 필드로 선언)를 공유합니다.
249
249
 
250
250
  이와 별도로 `SessionStart` 훅은 npm 레지스트리와 Claude Code 플러그인 마켓플레이스를 백그라운드에서 확인합니다(세션 시작을 막지 않습니다). 새 버전이 게시되어 있으면 다음 세션 시작 시 "Update available!" 안내가 한 줄 표시됩니다. `HYPO_NO_UPDATE_CHECK=1`, `NO_UPDATE_NOTIFIER=1`을 지정하거나 `CI=true` 환경에서 실행하면 점검을 건너뜁니다.
251
251
 
252
- 위 네 가지 레인 외의 v1.3 세부 수정 — 세션 마무리 lint를 건드린 파일로 스코프해 무관한 debt로 `/compact`가 막히지 않게 한 변경(ADR 0037), `feedback` scope 검증기가 cwd 유래 project id를 수용하게 한 수정(OQ-34), `--strict`가 에러로 승격하는 안정적 lint 경고 ID `W1`/`W2`/`W4`(`--fix`로 자동복구되는 `W3`는 경고로 유지) — 은 [`CHANGELOG.md`](CHANGELOG.md)를 참고하세요.
252
+ 위 네 가지 레인 외의 v1.3 세부 수정 — 세션 마무리 lint를 건드린 파일로 스코프해 무관한 debt로 `/compact`가 막히지 않게 한 변경, `feedback` scope 검증기가 cwd 유래 project id를 수용하게 한 수정, `--strict`가 에러로 승격하는 안정적 lint 경고 ID `W1`/`W2`/`W4`(`--fix`로 자동복구되는 `W3`는 경고로 유지) — 은 [`CHANGELOG.md`](CHANGELOG.md)를 참고하세요. **v1.3.1**은 수정 전용 패치입니다: 업데이트 notifier 배너가 이제 보이지 않던 stderr 출력 대신 top-level `systemMessage` 채널로 실제 사용자에게 도달하고, `/hypo:upgrade`가 플러그인·dual(수동+플러그인) 설치에서 core 훅을 중복 등록하지 않으며, 세션 마무리가 두 프로젝트가 같은 최신 날짜를 가질 때 완료된 close를 false-block 하지 않습니다. **v1.3.2**는 비-프로젝트(툴링·위키 전용) 세션을 무관한 프로젝트에 엮지 않고 닫는 1급 log-only close 경로와, 페이지 이름을 바꿀 때 해당하는 인바운드 위키링크를 갱신하는 `rename` 헬퍼를 추가합니다(모호하거나 append-only인 참조는 갱신하지 않고 보고합니다). 또한 세션 마무리 게이트(오늘 활동한 모든 프로젝트를 게이트, 세션별 마커와 `/compact`가 하나의 게이트를 공유, 도출 가능한 루트 `log.md` 항목 자동 도출)와 linter(`--json`이 파이프에서 잘리지 않음, 볼트 관습 위키링크를 오탐 대신 정상 해석)를 수정합니다.
253
253
 
254
254
  ### 셋업 & 유지보수
255
255
 
@@ -348,7 +348,7 @@ Claude가 잘못한 순간 — 또는 반대로 정확히 잘한 순간 — `/hy
348
348
 
349
349
  > **모델 사업자에게 전송되는 범위 안내.** Hypomnema 훅은 위키 본문을 Claude Code의 추가 컨텍스트(`additionalContext`)에 실어 보내며, 이 내용은 프롬프트의 일부로 Claude 모델 사업자에게 전송됩니다. 따라서 `.hypoignore`에 등록된 경로는 모든 주입 훅(`hypo-file-watch`, `hypo-session-start`, `hypo-cwd-change`, `hypo-lookup`)과 `ingest`에서 제외되지만, 등록되지 *않은* 파일은 전송 대상이 됩니다. (`hypo-auto-stage`/`hypo-auto-commit`은 git 스테이징용 훅이라 컨텍스트를 주입하지는 않지만, 스테이징 판단에도 동일하게 `.hypoignore`를 참고합니다.) 비밀 정보는 위키에 두지 마시고, `HYPO_DIR` 아래에 민감한 내용을 저장하기 전에 `.hypoignore` 패턴을 먼저 점검하시기 바랍니다.
350
350
 
351
- > **git sync 범위.** Hypomnema는 `~/hypomnema/` 위키 자체만 git sync합니다. 단, `init` / `upgrade`는 `~/.claude/` 내부의 관리 대상 영역 — Hypomnema 자체 hook(`~/.claude/hooks/`), 슬래시 커맨드(`~/.claude/commands/hypo/`), `settings.json` 등록 — 을 설치·SHA 추적하며, v1.2.0 **extensions companion sync**(ADR 0024)에 의해 위키의 `~/hypomnema/extensions/`에 둔 `agents/`·`commands/`·`hooks/`·`skills/`도 자동 미러링합니다(`--codex` 옵션 시 `hooks`·`commands` 부분 집합만 `~/.codex/`로). 이 관리 대상 영역 *바깥*의 `~/.claude/` 콘텐츠는 의도적으로 Hypomnema가 **관리하지 않습니다** — 위키를 거치지 않는 기타 agent/skill, 머신 고유 `settings.local.json` 등 일반적인 Claude Code 설정 기기 간 동기화는 [chezmoi](https://www.chezmoi.io/) 같은 별도 dotfiles 매니저 사용을 권장합니다.
351
+ > **git sync 범위.** Hypomnema는 `~/hypomnema/` 위키 자체만 git sync합니다. 단, `init` / `upgrade`는 `~/.claude/` 내부의 관리 대상 영역 — Hypomnema 자체 hook(`~/.claude/hooks/`), 슬래시 커맨드(`~/.claude/commands/hypo/`), `settings.json` 등록 — 을 설치·SHA 추적하며, v1.2.0 **extensions companion sync**에 의해 위키의 `~/hypomnema/extensions/`에 둔 `agents/`·`commands/`·`hooks/`·`skills/`도 자동 미러링합니다(`--codex` 옵션 시 `hooks`·`commands` 부분 집합만 `~/.codex/`로). 이 관리 대상 영역 *바깥*의 `~/.claude/` 콘텐츠는 의도적으로 Hypomnema가 **관리하지 않습니다** — 위키를 거치지 않는 기타 agent/skill, 머신 고유 `settings.local.json` 등 일반적인 Claude Code 설정 기기 간 동기화는 [chezmoi](https://www.chezmoi.io/) 같은 별도 dotfiles 매니저 사용을 권장합니다.
352
352
 
353
353
  ### `/hypo:*` 커맨드는 어디서 오는가?
354
354
 
package/README.md CHANGED
@@ -24,14 +24,14 @@ _Make Claude take notes — and measure whether it actually does._
24
24
  > **Quick decoder for terms used below.** *frontmatter* = the YAML block at the top of a markdown file; *wikilink* = a `[[page-slug]]` cross-reference; *ADR* = "Architecture Decision Record", a short markdown page that records *why* a design choice was made; *projection* = a one-way derive (`pages/feedback/*.md` → `MEMORY.md` / `<learned_behaviors>`); *hook* = a script that Claude Code runs automatically on lifecycle events; *hot.md* / *session-state.md* = the per-project cache files that hold "what just happened" and "what's next" so a paused project resumes in one read. Full glossary lives under [Term decoder](#term-decoder).
25
25
 
26
26
  > **Current state vs. v2 vision.** v1.3.0 (today) is honest about its trigger model: most wiki behavior — ingest, query, session-close — still fires on **explicit `/hypo:*` commands**, but the auto-behavior surface is growing. The v2 thesis is *fully autonomous* — Claude reading, writing, and synthesizing the wiki without being asked. **v1.1.0** shipped the **observability score** that measures how often the wiki is actually used per session (ingest / query / session-close / citation rates). **v1.2.0** adds four load-bearing autonomous lanes on top:
27
- > - **feedback-as-SoT with one-way projections** — `pages/feedback/` becomes the single source-of-truth (SoT) for behavior corrections; the wiki one-way derives `MEMORY.md` and the `<learned_behaviors>` block inside `~/.claude/CLAUDE.md`, so you edit one place and the projections refresh on their own (ADR 0031 — full design rationale lives in `projects/hypomnema/decisions/0031-*.md` inside your wiki).
28
- > - **extensions companion sync** — anything you drop under `~/hypomnema/extensions/{agents,commands,hooks,skills}/` is mirrored into `~/.claude/` automatically; the optional `--codex` flag additionally mirrors `hooks` and `commands` into `~/.codex/` (agents/skills are Claude-only and skipped on the Codex target by design) (ADR 0024).
29
- > - **auto-project creation on cwd match** — when you `cd` into a git repo with a project marker (`package.json`, `Cargo.toml`, etc.) and no matching wiki project exists, Hypomnema offers to scaffold one for you (ADR 0023).
30
- > - **Stop-chain auto-minimal-crystallize + `/clear` recovery** — non-trivial sessions get an automatic "save a minimal session-close note?" prompt; `/clear` after a forgotten close is detected and recovered cleanly (ADR 0022).
27
+ > - **feedback-as-SoT with one-way projections** — `pages/feedback/` becomes the single source-of-truth (SoT) for behavior corrections; the wiki one-way derives `MEMORY.md` and the `<learned_behaviors>` block inside `~/.claude/CLAUDE.md`, so you edit one place and the projections refresh on their own.
28
+ > - **extensions companion sync** — anything you drop under `~/hypomnema/extensions/{agents,commands,hooks,skills}/` is mirrored into `~/.claude/` automatically; the optional `--codex` flag additionally mirrors `hooks` and `commands` into `~/.codex/` (agents/skills are Claude-only and skipped on the Codex target by design).
29
+ > - **auto-project creation on cwd match** — when you `cd` into a git repo with a project marker (`package.json`, `Cargo.toml`, etc.) and no matching wiki project exists, Hypomnema offers to scaffold one for you.
30
+ > - **Stop-chain auto-minimal-crystallize + `/clear` recovery** — non-trivial sessions get an automatic "save a minimal session-close note?" prompt; `/clear` after a forgotten close is detected and recovered cleanly.
31
31
  >
32
32
  > The schema (`SCHEMA.md`) bumps to 2.0 — the `feedback` page type now requires 9 mandatory frontmatter fields. `hypomnema upgrade --apply` writes `MIGRATION-v2.0.md` into the wiki root with a step-by-step backfill checklist. Your own `SCHEMA.md` is **never overwritten** by upgrade — we call this policy *Option C*: the upgrade only tells you what changed, and you apply the diff yourself.
33
33
  >
34
- > **v1.3.0** refines this layer rather than expanding autonomy: the session-close flow gains four *advisory* reflections (they suggest, never auto-act — ADR 0029), `hypomnema lint --strict` promotes selected warnings to errors for release gates, and install hardens with **stale-sibling detection** — an older `hypomnema` on `$PATH` can no longer silently downgrade your active hooks (ADR 0038).
34
+ > **v1.3.0** refines this layer rather than expanding autonomy: the session-close flow gains four *advisory* reflections (they suggest, never auto-act), `hypomnema lint --strict` promotes selected warnings to errors for release gates, and install hardens with **stale-sibling detection** — an older `hypomnema` on `$PATH` can no longer silently downgrade your active hooks.
35
35
 
36
36
  ---
37
37
 
@@ -45,7 +45,7 @@ Inside Claude Code:
45
45
 
46
46
  ```
47
47
  /plugin marketplace add sk-lim19f/Hypomnema
48
- /plugin install hypomnema@hypomnema
48
+ /plugin install hypo@hypomnema
49
49
  /hypo:init
50
50
  ```
51
51
 
@@ -240,16 +240,16 @@ Eight commands cover the full capture → retrieval → consolidation cycle.
240
240
  | `hypo-auto-commit.mjs` | `Stop` | Auto commit + pull + push |
241
241
  | `hypo-hot-rebuild.mjs` | `Stop` | Rebuild `hot.md` |
242
242
  | `hypo-personal-check.mjs` | `PreCompact` | Block compact on lint failures or unfinished session-close |
243
- | `hypo-session-end.mjs` | `SessionEnd` | Write a SessionEnd marker so SessionStart can detect `source=clear` recovery (ADR 0022) |
243
+ | `hypo-session-end.mjs` | `SessionEnd` | Write a SessionEnd marker so SessionStart can detect `source=clear` recovery |
244
244
  | `hypo-session-record.mjs` | `Stop` | Record session metadata for the observability score and auto-resume signaling |
245
- | `hypo-auto-minimal-crystallize.mjs` | `Stop` | Offer (and on consent run) `/hypo:crystallize --apply-session-close --minimal` after non-trivial sessions (ADR 0022 Layer 3) |
245
+ | `hypo-auto-minimal-crystallize.mjs` | `Stop` | Offer (and on consent run) `/hypo:crystallize --apply-session-close --minimal` after non-trivial sessions |
246
246
  | `hypo-web-fetch-ingest.mjs` | `PostToolUse(WebFetch/WebSearch)` | Inject a `/hypo:ingest` nudge into `additionalContext` after a URL resolution (privacy-aware: redacts query/hash/userinfo) |
247
247
 
248
248
  All hooks resolve the wiki root via `HYPO_DIR` env → `hypo-config.md` scan → `~/hypomnema` default, and share `hypo-shared.mjs` (declared via `hooks.json`'s `shared` field).
249
249
 
250
250
  Additionally, the `SessionStart` hook performs a non-blocking background check against npm and the Claude Code plugin marketplace and prints an "Update available!" banner the next time a newer Hypomnema version has been published. Opt out with `HYPO_NO_UPDATE_CHECK=1`, `NO_UPDATE_NOTIFIER=1`, or by running under `CI=true`.
251
251
 
252
- For fix-level v1.3 detail beyond the lanes above — session-close lint scoped to touched files so `/compact` is no longer blocked by unrelated debt (ADR 0037), the `feedback` scope validator accepting cwd-derived project ids (OQ-34), and the stable lint warning IDs `W1`/`W2`/`W4` that `--strict` promotes to errors (while `W3`, auto-repaired by `--fix`, stays a warning) — see [`CHANGELOG.md`](CHANGELOG.md).
252
+ For fix-level v1.3 detail beyond the lanes above — session-close lint scoped to touched files so `/compact` is no longer blocked by unrelated debt, the `feedback` scope validator accepting cwd-derived project ids, and the stable lint warning IDs `W1`/`W2`/`W4` that `--strict` promotes to errors (while `W3`, auto-repaired by `--fix`, stays a warning) — see [`CHANGELOG.md`](CHANGELOG.md). **v1.3.1** is a fixes-only patch: update-notifier banners now actually reach the user via the top-level `systemMessage` channel instead of an invisible stderr write, `/hypo:upgrade` no longer double-registers core hooks for plugin or dual (manual + plugin) installs, and session-close no longer false-blocks a completed close when two projects share the latest date. **v1.3.2** adds a first-class log-only session-close path, so a non-project (tooling or wiki-only) session closes cleanly without being forced onto an unrelated project, plus a `rename` helper that rewrites the eligible inbound wikilinks when you rename a page (ambiguous or append-only references are reported, not rewritten). It also tightens the session-close gate (every project with activity today is gated, the per-session marker and `/compact` share one gate, and the derivable root `log.md` entry auto-derives) and the linter (`--json` no longer truncates on a pipe, and vault-convention wikilinks resolve instead of being false-flagged).
253
253
 
254
254
  ### Setup & maintenance
255
255
 
@@ -348,7 +348,7 @@ Place a `hypo-config.md` at the wiki root to make it portable across machines wi
348
348
 
349
349
  > **Provider transmission disclaimer.** Hypomnema hooks emit wiki content into Claude Code's `additionalContext`, which is transmitted to the Claude model provider as part of the prompt. `.hypoignore` is enforced at every content-injection hook (`hypo-file-watch`, `hypo-session-start`, `hypo-cwd-change`, `hypo-lookup`) and at `ingest`, but any file *not* matched by `.hypoignore` is fair game for transmission. (`hypo-auto-stage` and `hypo-auto-commit` are git-staging hooks, not injection points, and also honor `.hypoignore` for their staging decisions.) Keep secrets out of the wiki, and review `.hypoignore` patterns before storing anything sensitive under `HYPO_DIR`.
350
350
 
351
- > **Scope of git sync.** Hypomnema git-syncs only the `~/hypomnema/` wiki itself. `init` / `upgrade` actively install and SHA-track a defined surface inside `~/.claude/` — Hypomnema's own hooks (`~/.claude/hooks/`), slash commands (`~/.claude/commands/hypo/`), and `settings.json` registrations — plus, via v1.2.0 **extensions companion sync** (ADR 0024), any `agents/` · `commands/` · `hooks/` · `skills/` you ship inside `~/hypomnema/extensions/` (and with `--codex`, the `hooks` + `commands` subset into `~/.codex/`). Anything *outside* that defined surface in `~/.claude/` is intentionally **not** managed by Hypomnema — for general cross-machine sync of Claude Code configuration (other agents/skills not staged via the wiki, machine-specific `settings.local.json`, etc.), the recommended pattern is still a separate dotfiles manager such as [chezmoi](https://www.chezmoi.io/).
351
+ > **Scope of git sync.** Hypomnema git-syncs only the `~/hypomnema/` wiki itself. `init` / `upgrade` actively install and SHA-track a defined surface inside `~/.claude/` — Hypomnema's own hooks (`~/.claude/hooks/`), slash commands (`~/.claude/commands/hypo/`), and `settings.json` registrations — plus, via v1.2.0 **extensions companion sync**, any `agents/` · `commands/` · `hooks/` · `skills/` you ship inside `~/hypomnema/extensions/` (and with `--codex`, the `hooks` + `commands` subset into `~/.codex/`). Anything *outside* that defined surface in `~/.claude/` is intentionally **not** managed by Hypomnema — for general cross-machine sync of Claude Code configuration (other agents/skills not staged via the wiki, machine-specific `settings.local.json`, etc.), the recommended pattern is still a separate dotfiles manager such as [chezmoi](https://www.chezmoi.io/).
352
352
 
353
353
  ### Where do `/hypo:*` commands live?
354
354
 
@@ -20,8 +20,8 @@ If the user invoked `/hypo:crystallize` to close a session (phrases like "세션
20
20
  Before composing the payload (Step 2), run these four reflections and surface each to the user. Every one is **advisory** (ADR 0029 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, record `ADR 없음 — <one-line reason>` in that same `sessionLog` entry. **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)** — If `projects/<name>/design-history.md` exists and this session changed design decisions it does not yet reflect, recommend updating it (the W8 lint warning flags this mechanically; an active-project W8 can also block at PreCompact). 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.
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.
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.
@@ -30,7 +30,7 @@ These are judgment calls; when uncertain, surface the question rather than skip
30
30
 
31
31
  ## Step 2 — Compose the session-close payload
32
32
 
33
- The session-close path is **payload-driven** (fix #38). Instead of writing the 5 mandatory files one-by-one, you compose a single JSON payload that describes the full session-close state, then hand it to `crystallize.mjs --apply-session-close`, which performs idempotent atomic writes and gates the result with lint.
33
+ The session-close path is **payload-driven**. Instead of writing the 5 mandatory files one-by-one, you compose a single JSON payload that describes the full session-close state, then hand it to `crystallize.mjs --apply-session-close`, which performs idempotent atomic writes and gates the result with lint.
34
34
 
35
35
  Payload shape (5 required + 1 conditional, per Spec §5.2.7 / §8.3 + ADR 0029):
36
36
 
@@ -41,7 +41,7 @@ Payload shape (5 required + 1 conditional, per Spec §5.2.7 / §8.3 + ADR 0029):
41
41
  "sessionState": { "content": "<full body of projects/<name>/session-state.md>" },
42
42
  "projectHot": { "content": "<full body of projects/<name>/hot.md>" },
43
43
  "rootHot": { "content": "<full body of <hypo-root>/hot.md>" },
44
- "sessionLog": { "entry": "<entry to append to projects/<name>/session-log/YYYY-MM.md>" },
44
+ "sessionLog": { "entry": "<entry to append to projects/<name>/session-log/YYYY-MM-DD.md (daily shard)>" },
45
45
  "log": { "entry": "<entry to append to <hypo-root>/log.md>" },
46
46
  "openQuestions": { "content": "<full body of pages/open-questions.md>" }
47
47
  }
@@ -67,7 +67,7 @@ Content guidance for each slot:
67
67
  1. **sessionState** — next tasks list for the upcoming session (what to tackle first next time).
68
68
  2. **projectHot** — session snapshot under 500 words: what changed and decisions made. Do **not** put next-step tasks here; those belong in `sessionState`.
69
69
  3. **rootHot** — active-projects pointer table with this project's `Last Session` date set to today.
70
- 4. **sessionLog** — one session entry to append to `projects/<name>/session-log/YYYY-MM.md`.
70
+ 4. **sessionLog** — one session entry to append to `projects/<name>/session-log/YYYY-MM-DD.md` (daily shard).
71
71
  5. **log** — one `session` entry to append to `<hypo-root>/log.md`.
72
72
  6. **openQuestions** (conditional) — only if `pages/open-questions.md` exists and questions were raised or resolved this session.
73
73
 
@@ -84,7 +84,7 @@ node <package-root>/scripts/crystallize.mjs \
84
84
  --json
85
85
  ```
86
86
 
87
- **`--session-id` (fix #27 PR-C):** pass the current session's id whenever you
87
+ **`--session-id`:** pass the current session's id whenever you
88
88
  know it — most importantly when this close was triggered by a `[WIKI_AUTOCLOSE]`
89
89
  Stop-hook block (the block reason prints the exact `--session-id` to use). On a
90
90
  verified close (`ok: true` + clean git tree), it writes the per-session marker
@@ -93,7 +93,7 @@ Stop-chain Layer 3 hook (`hypo-auto-minimal-crystallize`) the session is closed,
93
93
  so it stops re-prompting. Omit it only when running crystallize purely for
94
94
  synthesis (no session-close intent) — the marker is then simply not written.
95
95
 
96
- **Behavior (fix #39 option D + fix #40 lint gates):**
96
+ **Behavior (option D + lint gates):**
97
97
 
98
98
  | Invocation | Behavior |
99
99
  |---|---|
@@ -102,7 +102,7 @@ synthesis (no session-close intent) — the marker is then simply not written.
102
102
  | `--apply-session-close --payload=<path> --session-id=<id>` | Same as above, **plus** writes the per-session closed marker on success (clean git required). The Stop-chain Layer 3 path. |
103
103
  | `--apply-session-close --force` | Skips the probe early-exit. `--payload` still required for any actual apply work. |
104
104
 
105
- **Two lint gates run automatically (fix #40), scoped to the files this close writes:**
105
+ **Two lint gates run automatically, scoped to the files this close writes:**
106
106
 
107
107
  Both gates judge only the **payload files** (the 5 mandatory close files + `open-questions.md`). Lint debt in other projects or shared `pages/` this close did not author is reported as a non-blocking `notices[]` entry, never gated — so an unrelated broken page elsewhere cannot block your close.
108
108
 
@@ -2,7 +2,7 @@
2
2
  description: Record an AI behavior correction or preference into the wiki
3
3
  ---
4
4
 
5
- You are running `/hypo:feedback`. Capture a behavior correction or preference into `pages/feedback/` — the **single source of truth** for learned behaviors (ADR 0031 / fix #37).
5
+ You are running `/hypo:feedback`. Capture a behavior correction or preference into `pages/feedback/` — the **single source of truth** for learned behaviors (ADR 0031).
6
6
 
7
7
  ## What this does
8
8
 
@@ -20,7 +20,7 @@ If the user named a project in the command invocation, use that. Otherwise, loca
20
20
  node <package-root>/scripts/resume.mjs [--hypo-dir="<path>"] [--project=<name>]
21
21
  ```
22
22
 
23
- The script will resolve the most recently active project from `hot.md` if `--project` is omitted.
23
+ When `--project` is omitted, the script prefers the project whose `working_dir` contains the current directory (cwd-first); if nothing under the current directory matches, it falls back to the most recently active project from `hot.md`.
24
24
 
25
25
  ---
26
26
 
@@ -80,7 +80,7 @@ The synthesis-heavy commands (`ingest`, `query`, `crystallize`, `lint`, `verify`
80
80
 
81
81
  ## Agent Skills
82
82
 
83
- `skills/<name>/SKILL.md` follows the Claude Agent Skills convention (per ADR `decisions/0001` in the wiki). When a conversation matches the skill's description, Claude auto-loads it without needing the slash command.
83
+ `skills/<name>/SKILL.md` follows the Claude Agent Skills convention. When a conversation matches the skill's description, Claude auto-loads it without needing the slash command.
84
84
 
85
85
  > v1.0 originally planned flat `skills/*.md` files; v1.1 switched to `<name>/SKILL.md` for compatibility with the official Agent Skills loader.
86
86
 
@@ -179,7 +179,7 @@ Helpers used by command scripts only. Not deployed to hooks.
179
179
  │ ├── hot.md ← project-level current state
180
180
  │ ├── session-state.md ← next tasks + last session summary
181
181
  │ └── session-log/
182
- │ └── YYYY-MM.md ← append-only monthly session log
182
+ │ └── YYYY-MM-DD.md ← append-only daily session-log shard
183
183
  ├── journal/
184
184
  │ ├── daily/
185
185
  │ ├── weekly/
@@ -331,7 +331,7 @@ scripts/weekly-report.mjs ← aggregated weekly autonomy score
331
331
  journal/weekly/<YYYY-Www>.md ← committed report (heuristic v0, spec §6.4 SoT)
332
332
  ```
333
333
 
334
- ### Transcript dual-source (ADR 0019)
334
+ ### Transcript dual-source
335
335
 
336
336
  `session-audit.mjs` reads transcripts from two locations, in priority order:
337
337
 
@@ -142,7 +142,7 @@ npm run fix:verify # Phase 1 of learned_behavior #6 — verifies fix #N status c
142
142
  When a test verifies behavior tied to a numbered fix in the wiki spec, add an anchor immediately above the `suite(...)` or `test(...)` call:
143
143
 
144
144
  ```js
145
- // @fix #25: replay-compact-guard-detects-slash-clear: /clear with incomplete wiki → WIKI_AUTOCLOSE
145
+ // @fix #N: replay-compact-guard-detects-slash-clear: /clear with incomplete wiki → WIKI_AUTOCLOSE
146
146
  test('replay-compact-guard-detects-slash-clear: /clear with incomplete wiki → WIKI_AUTOCLOSE', () => { ... });
147
147
  ```
148
148
 
@@ -317,7 +317,7 @@ Major bumps must include an `upgrade.mjs` migration fixture in `tests/runner.mjs
317
317
  These documents live in the maintainer's personal wiki, not in this repo:
318
318
 
319
319
  - `~/hypomnema/projects/hypomnema/prd-v1.1.md` — current product requirements
320
- - `~/hypomnema/projects/hypomnema/decisions/0001..0014.md` — architecture decision records
320
+ - `~/hypomnema/projects/hypomnema/decisions/*.md` — architecture decision records
321
321
  - `~/hypomnema/projects/hypomnema/design-history.md` — narrative design history
322
322
  - `~/hypomnema/projects/hypomnema/backlog-v1.0.md` — historical gap analysis (archived)
323
323
  - `~/hypomnema/projects/hypomnema/test-cases-v1.0.md` — historical QA spec (archived)
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * hypo-auto-minimal-crystallize.mjs — Stop hook (fix #27 PR-C, ADR 0022 Layer 3)
3
+ * hypo-auto-minimal-crystallize.mjs — Stop hook (ADR 0022 Layer 3)
4
4
  *
5
5
  * Last hook in the Stop chain: a final-line defense that blocks `Stop` when
6
- * the current session performed mutation work but never produced a verified
6
+ * the current session did substantial work (mutation, or a high-volume
7
+ * read-only investigation — see step 3) but never produced a verified
7
8
  * session-close. Forces Claude to run minimal session-close before the
8
9
  * conversation context evaporates.
9
10
  *
@@ -11,8 +12,10 @@
11
12
  *
12
13
  * 1. stop_hook_active === true → continue (loop guard; PoC 2026-05-14)
13
14
  * 2. wiki absent → continue (fail-open)
14
- * 3. transcript has zero Edit/Write/MultiEdit/NotebookEdit tool_use
15
- * continue (substantial-session gate)
15
+ * 3. not a substantial session → continue (substantial-session gate)
16
+ * substantial = ≥1 mutation tool_use, OR ≥5 read-only investigation
17
+ * calls (Read/Grep/Glob/Bash) — 6a, so read-only review/debug sessions
18
+ * are also nudged to close. Pure Q&A / incidental lookups still skip.
16
19
  * 4. no recent user close-intent → continue (close-intent gate, see below)
17
20
  * 5. readSessionClosedMarker(session_id) valid
18
21
  * → continue (close already verified)
@@ -43,18 +46,19 @@ import { join } from 'path';
43
46
  import {
44
47
  HYPO_DIR,
45
48
  PKG_ROOT,
46
- hasMutatingTranscriptActivity,
49
+ isSubstantialSession,
47
50
  readSessionClosedMarker,
48
51
  extractUserMessages,
49
52
  isClosePattern,
50
53
  isGateSkipped,
54
+ precompactGateStatus,
51
55
  } from './hypo-shared.mjs';
52
56
 
53
57
  function emitContinue() {
54
58
  console.log(JSON.stringify({ continue: true, suppressOutput: true }));
55
59
  }
56
60
 
57
- function emitBlock(sessionId, transcriptPath) {
61
+ function emitBlock(sessionId, transcriptPath, gate = null) {
58
62
  // One-line, skill-first. /hypo:crystallize is the documented session-close
59
63
  // alias; passing --session-id there writes the per-session marker that clears
60
64
  // this block. CLI fallback + bypass live in commands/crystallize.md, not here
@@ -64,12 +68,39 @@ function emitBlock(sessionId, transcriptPath) {
64
68
  // coherence: a marker written without lint would only let Stop pass for
65
69
  // /compact to immediately re-block on the same errors).
66
70
  const transcriptHint = transcriptPath ? ` --transcript-path=${transcriptPath}` : '';
67
- const reason = `[WIKI_AUTOCLOSE] session-close 미완료 — /hypo:crystallize 실행으로 마무리 (session_id=${sessionId}${transcriptHint}).`;
71
+ const markCmd = `crystallize --mark-session-closed --session-id=${sessionId}${transcriptHint}`;
72
+ // The log-only escape hatch for a non-project (wiki/tooling-only)
73
+ // session. Offered ONLY as an explicit alternative when a close blocker is
74
+ // present — never as the default recovery, so a real project session is not
75
+ // taught to bypass the ADR 0043 close invariant (codex design Finding 3).
76
+ const logOnlyCmd = `crystallize --mark-session-closed --log-only --session-id=${sessionId}${transcriptHint}`;
77
+ // ADR 0047: refine the message with the read-only /compact gate result.
78
+ // - gate green → the close is compact-ready and ONLY the marker is missing
79
+ // (the hand-edit close case: files Written + committed directly, bypassing
80
+ // the marker writer). Say so precisely + give the one command, instead of
81
+ // the generic "미완료" that reads as "you never closed".
82
+ // - gate has blockers → surface them so the user fixes the real issue first.
83
+ // - gate null (tooling error/unavailable) → generic message (fail-open).
84
+ let reason;
85
+ if (gate && gate.ok) {
86
+ reason = `[WIKI_AUTOCLOSE] close gate green — only the session-closed marker is missing. Run \`${markCmd}\` to finish (session_id=${sessionId}).`;
87
+ } else if (gate && gate.blockers && gate.blockers.length > 0) {
88
+ const blockers = gate.blockers.map((b) => b.reason).join('; ');
89
+ reason = `[WIKI_AUTOCLOSE] session-close incomplete — resolve: ${blockers}. Then run \`${markCmd}\` (session_id=${sessionId}).`;
90
+ // Only when a project-close blocker is what's holding the session: a
91
+ // non-project session has nothing to close, so offer log-only as the way out
92
+ // (Claude decides whether this session is project-scoped — no auto-attribution).
93
+ if (gate.blockers.some((b) => b.type === 'close')) {
94
+ reason += ` If this was a non-project (wiki/tooling-only) session with no project to close, run \`${logOnlyCmd}\` instead (log-only close, no project attribution).`;
95
+ }
96
+ } else {
97
+ reason = `[WIKI_AUTOCLOSE] session-close 미완료 — /hypo:crystallize 실행으로 마무리 (session_id=${sessionId}${transcriptHint}).`;
98
+ }
68
99
  console.log(
69
100
  JSON.stringify({
70
101
  decision: 'block',
71
102
  reason,
72
- stopReason: 'session-close incomplete (fix #27 PR-C / ADR 0022 Layer 3)',
103
+ stopReason: 'session-close incomplete (ADR 0022 Layer 3)',
73
104
  }),
74
105
  );
75
106
  }
@@ -112,8 +143,10 @@ process.stdin.on('end', () => {
112
143
  const sessionId = payload.session_id || payload.sessionId || null;
113
144
  const transcriptPath = payload.transcript_path || payload.transcriptPath || null;
114
145
 
115
- // 3. substantial-session gate. Read-only / Q&A sessions skip the block.
116
- if (!hasMutatingTranscriptActivity(transcriptPath)) {
146
+ // 3. substantial-session gate. Pure Q&A / incidental-lookup sessions skip
147
+ // the block; mutating sessions AND high-volume read-only investigations
148
+ // (6a) pass through to the close-intent gate.
149
+ if (!isSubstantialSession(transcriptPath)) {
117
150
  emitContinue();
118
151
  return;
119
152
  }
@@ -141,7 +174,20 @@ process.stdin.on('end', () => {
141
174
  return;
142
175
  }
143
176
 
144
- emitBlock(sessionId, transcriptPath);
177
+ // ADR 0047: read-only /compact gate (same precompactGateStatus the real
178
+ // PreCompact hook uses) sharpens the block message — distinguishes "close
179
+ // is compact-ready, only the marker is missing" from "there are real
180
+ // blockers". The hook NEVER writes the marker here (file-header invariant);
181
+ // this is read-only. Any error → null → emitBlock falls back to the generic
182
+ // message (fail-open).
183
+ let gate = null;
184
+ try {
185
+ gate = precompactGateStatus(HYPO_DIR, transcriptPath ? { transcriptPath } : {});
186
+ } catch {
187
+ gate = null;
188
+ }
189
+
190
+ emitBlock(sessionId, transcriptPath, gate);
145
191
  } catch (err) {
146
192
  // Fail-open on any unexpected error.
147
193
  process.stderr.write(`[hypo-auto-minimal-crystallize] error: ${err?.message ?? String(err)}\n`);
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * hypo-compact-guard.mjs — UserPromptSubmit hook
4
4
  *
5
- * Scope: detects "/compact" or "/clear" typed in chat only (ADR 0022 Layer 2, fix #25).
5
+ * Scope: detects "/compact" or "/clear" typed in chat only (ADR 0022 Layer 2).
6
6
  * The CLI built-in /compact does NOT fire UserPromptSubmit — use personal-wiki-check.mjs
7
7
  * (PreCompact hook) as the hard gate for that path. /clear has no PreCompact event, so
8
8
  * this hook is the only chat-side gate that can prompt session-close before context wipe.
@@ -137,7 +137,7 @@ process.stdin.on('end', () => {
137
137
  return;
138
138
  }
139
139
 
140
- // MISS: cwd matches no project. fix #23 / ADR 0023 — offer to create one
140
+ // MISS: cwd matches no project. ADR 0023 — offer to create one
141
141
  // when the trigger conditions hold. Same nudge-only model as session-start.
142
142
  let suggestPrefix = '';
143
143
  if (shouldSuggestProjectCreation(newCwd, HYPO_DIR)) {
@@ -3,9 +3,9 @@
3
3
  * hypo-first-prompt.mjs — UserPromptSubmit hook
4
4
  *
5
5
  * Consumes the marker written by hypo-session-start.mjs (source omitted /
6
- * 'session-start') or hypo-cwd-change.mjs (source 'cwd-change', fix #13).
6
+ * 'session-start') or hypo-cwd-change.mjs (source 'cwd-change').
7
7
  * On the FIRST user prompt after the marker is written, FORCES a one-line
8
- * resume summary into the reply (fix #3 — the old "answer only if related"
8
+ * resume summary into the reply (the old "answer only if related"
9
9
  * conditional is removed; the line is injected unconditionally).
10
10
  *
11
11
  * hot.md / session-state.md content is NOT re-injected here — the upstream
@@ -12,7 +12,12 @@
12
12
 
13
13
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
14
14
  import { join } from 'path';
15
- import { HYPO_DIR, computeSessionGrowth, formatGrowthMetrics } from './hypo-shared.mjs';
15
+ import {
16
+ HYPO_DIR,
17
+ computeSessionGrowth,
18
+ formatGrowthMetrics,
19
+ deriveRootLogEntries,
20
+ } from './hypo-shared.mjs';
16
21
 
17
22
  const HOT_PATH = join(HYPO_DIR, 'hot.md');
18
23
  const GROWTH_CACHE = join(HYPO_DIR, '.cache', 'last-session-growth.json');
@@ -107,6 +112,14 @@ try {
107
112
  } catch (err) {
108
113
  process.stderr.write(`[hypo-hot-rebuild] error: ${err?.message ?? String(err)}\n`);
109
114
  }
115
+ // Auto-derive the root log.md session entry from each project's session-log
116
+ // heading (runs AFTER rebuild() so root hot.md is already fresh and isn't itself
117
+ // counted as the project's open gate problem). Best-effort: own try/catch.
118
+ try {
119
+ deriveRootLogEntries(HYPO_DIR);
120
+ } catch (err) {
121
+ process.stderr.write(`[hypo-hot-rebuild] log-derive error: ${err?.message ?? String(err)}\n`);
122
+ }
110
123
  try {
111
124
  emitGrowth();
112
125
  } catch (err) {