hypomnema 1.4.2 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ko.md +35 -18
  4. package/README.md +25 -8
  5. package/commands/audit.md +2 -2
  6. package/commands/crystallize.md +22 -14
  7. package/commands/doctor.md +1 -1
  8. package/commands/feedback.md +2 -2
  9. package/commands/graph.md +1 -1
  10. package/commands/ingest.md +1 -1
  11. package/commands/init.md +2 -2
  12. package/commands/lint.md +1 -1
  13. package/commands/query.md +1 -1
  14. package/commands/rename.md +1 -1
  15. package/commands/resume.md +1 -1
  16. package/commands/stats.md +1 -1
  17. package/commands/uninstall.md +4 -2
  18. package/commands/upgrade.md +1 -1
  19. package/commands/verify.md +1 -1
  20. package/docs/ARCHITECTURE.md +4 -4
  21. package/docs/CONTRIBUTING.md +8 -7
  22. package/hooks/hypo-auto-commit.mjs +2 -2
  23. package/hooks/hypo-auto-minimal-crystallize.mjs +7 -7
  24. package/hooks/hypo-compact-guard.mjs +2 -2
  25. package/hooks/hypo-cwd-change.mjs +27 -37
  26. package/hooks/hypo-lookup.mjs +37 -2
  27. package/hooks/hypo-personal-check.mjs +37 -22
  28. package/hooks/hypo-session-end.mjs +1 -1
  29. package/hooks/hypo-session-record.mjs +2 -2
  30. package/hooks/hypo-session-start.mjs +62 -42
  31. package/hooks/hypo-shared.mjs +430 -85
  32. package/hooks/version-check.mjs +1 -1
  33. package/package.json +5 -1
  34. package/scripts/check-tracker-ids.mjs +69 -31
  35. package/scripts/crystallize.mjs +151 -53
  36. package/scripts/doctor.mjs +7 -7
  37. package/scripts/feedback-sync.mjs +5 -5
  38. package/scripts/feedback.mjs +9 -10
  39. package/scripts/init.mjs +7 -7
  40. package/scripts/lib/check-tracker-ids.mjs +90 -32
  41. package/scripts/lib/design-history-stale.mjs +1 -1
  42. package/scripts/lib/extensions.mjs +7 -7
  43. package/scripts/lib/failure-type.mjs +1 -1
  44. package/scripts/lib/feedback-scope.mjs +1 -1
  45. package/scripts/lib/page-usage.mjs +141 -0
  46. package/scripts/lib/project-create.mjs +2 -2
  47. package/scripts/lib/template-schema-version.mjs +1 -1
  48. package/scripts/lib/wd-match.mjs +181 -0
  49. package/scripts/lib/wikilink.mjs +1 -1
  50. package/scripts/lint.mjs +5 -5
  51. package/scripts/rename.mjs +1 -1
  52. package/scripts/resume.mjs +20 -22
  53. package/scripts/session-audit.mjs +1 -1
  54. package/scripts/stats.mjs +3 -3
  55. package/scripts/uninstall.mjs +3 -3
  56. package/scripts/upgrade.mjs +85 -18
  57. package/skills/crystallize/SKILL.md +17 -11
  58. package/skills/graph/SKILL.md +3 -3
  59. package/skills/ingest/SKILL.md +5 -5
  60. package/skills/lint/SKILL.md +3 -3
  61. package/skills/query/SKILL.md +3 -3
  62. package/skills/verify/SKILL.md +3 -3
  63. package/templates/SCHEMA.md +1 -1
  64. package/templates/hypo-config.md +1 -1
  65. package/templates/hypo-guide.md +21 -15
  66. package/templates/projects/_template/hot.md +1 -1
  67. package/scripts/fix-status-verify.mjs +0 -256
  68. package/scripts/lib/adr-corpus.mjs +0 -79
  69. package/scripts/lib/fix-manifest.mjs +0 -109
  70. package/scripts/lib/fix-status-verify.mjs +0 -439
@@ -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.4.2",
14
+ "version": "1.5.1",
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.4.2",
3
+ "version": "1.5.1",
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
@@ -15,7 +15,7 @@
15
15
 
16
16
  Claude Code를 위한 LLM 네이티브 개인 위키. 쓸수록 지식이 쌓입니다.
17
17
 
18
- _Claude에게 기록을 맡기고, 그 기록이 실제로 쌓이는지 측정하세요._
18
+ Claude에게 기록을 맡기고, 그 기록이 실제로 쌓이는지 측정하세요.
19
19
 
20
20
  [빠른 시작](#빠른-시작) • [다른 시스템과의 비교](#다른-시스템과의-비교) • [설계 결정](#설계-결정) • [기능](#기능) • [아키텍처](docs/ARCHITECTURE.md) • [기여](docs/CONTRIBUTING.md)
21
21
 
@@ -25,14 +25,20 @@ Andrej Karpathy의 "LLM 네이티브 위키" 스케치에서 출발했습니다.
25
25
 
26
26
  ### 지금 어디까지 자동인가
27
27
 
28
- 현재 릴리스는 v1.4.2이고, 세션 마무리 경로를 견고하게 다듬었습니다: 마무리가 올바른 프로젝트에 기록되고, 미등록 태그나 수기 `log.md` 엔트리가 끝난 마무리를 더는 잘못 막지 않으며, 안내하는 복구 명령이 플러그인 설치에서도 그대로 실행됩니다. 위키 작업(자료 정리·검색·세션 마무리)은 아직 `/hypo:*` 명령어를 직접 입력해 시작합니다. v2의 목표는 Claude가 시키지 않아도 위키를 읽고 쓰고 합성하는 완전 자율 동작이고, 지금은 그쪽으로 가는 중입니다. v1.1.0 위키가 세션에서 얼마나 쓰였는지 재는 관측성 점수를 넣었습니다. v1.2.0은 자동으로 동작하는 영역 가지를 추가했습니다.
28
+ 현재 릴리스는 v1.5.1입니다. 위키가 자기 신선도를 알리는 방식을 다듬었습니다. `verify_by_date`가 지난 페이지는 주입되는 순간 `[STALE ...]`로 표시돼, 날짜 지난 답을 조용히 신뢰하는 대신 눈에 띄게 하고, lookup 사용을 로컬에서 추적해 링크됐지만 번도 주입되지 않은 페이지를 crystallize가 cold 후보로 표면화합니다. 같은 릴리스에서 표준 `session | project: title` 콜론 로그 포맷을 stale로 오진하던 세션 마무리도 견고해졌습니다. 그 앞 v1.5.0 머신 안정성과 일상의 마찰에 집중했습니다. git 동기화 볼트의 `cwd`-first resume, 코드 레포 세션에 위키 위치 안내, 세션 마무리가 무관한 lint 부채를 다시 나열하지 않는 것이었습니다.
29
+
30
+ 위키 작업(자료 정리·검색·세션 마무리)은 아직 `/hypo:*` 명령이나 자연어로 시작합니다. v2의 목표는 Claude가 시키지 않아도 위키를 읽고 쓰고 합성하는 완전 자율 동작이고, 지금 그쪽으로 가는 중입니다.
31
+
32
+ 이미 자동으로 도는 영역은 다음과 같습니다. v1.1.0은 위키가 한 세션에서 얼마나 쓰였는지 재는 관측성 점수를 넣었고, v1.2.0은 네 영역을 자동화했습니다.
29
33
 
30
34
  - 피드백 한 곳만 고치면 나머지는 따라옵니다. `pages/feedback/`에 한 번 적으면 위키가 `MEMORY.md`와 `~/.claude/CLAUDE.md`의 `<learned_behaviors>` 블록을 자동으로 갱신합니다(단방향 projection).
31
35
  - 확장 파일도 함께 동기화. 위키 안 `~/hypomnema/extensions/{agents,commands,hooks,skills}/`에 둔 파일을 `~/.claude/`에 자동 반영합니다. `--codex`를 붙이면 `hooks`·`commands`는 `~/.codex/`에도 반영하지만, `agents`와 `skills`는 Claude 전용이라 건너뜁니다.
32
36
  - 프로젝트 자동 생성. 프로젝트 표식(`package.json`·`Cargo.toml` 등)이 있는 git 저장소로 `cd` 했는데 대응하는 위키 프로젝트가 없으면 만들지 물어봅니다.
33
37
  - 세션 종료 자동 정리와 `/clear` 복구. 의미 있는 세션이 끝나면 "마무리 메모를 짧게 남길까요?"가 자동으로 뜹니다. 마무리하지 않은 채 `/clear`를 입력해도 다음 세션 시작 때 이어서 정리할 수 있습니다.
34
38
 
35
- 버전별로 무엇이 바뀌었는지는 [CHANGELOG](CHANGELOG.md)에 있습니다. 한 가지만 알아 두면, `hypomnema upgrade --apply`는 사용자가 직접 편집한 `SCHEMA.md`를 덮어쓰지 않습니다. 스키마가 올라가면 위키 루트에 마이그레이션 보고서를 써 주고, 실제 반영은 사용자가 직접 결정합니다(코드에서 _Option C_로 부르는 정책).
39
+ 버전별로 무엇이 바뀌었는지는 [CHANGELOG](CHANGELOG.md)에 있습니다.
40
+
41
+ 업그레이드 정책 하나만 미리 알아 두세요. `hypomnema upgrade --apply`는 사용자가 직접 편집한 `SCHEMA.md`를 덮어쓰지 않습니다. 스키마가 올라가면 위키 루트에 마이그레이션 보고서만 써 주고, 실제 반영은 사용자가 직접 결정합니다(코드에서 Option C라고 부르는 정책).
36
42
 
37
43
  ---
38
44
 
@@ -75,6 +81,8 @@ hypomnema
75
81
 
76
82
  나머지는 훅이 합니다. 자동 staging, 자동 commit/push, 세션 상태 주입, 관련 노트 룩업까지 알아서 처리합니다.
77
83
 
84
+ > 명령어를 외울 필요는 없습니다. 하려는 일을 평범한 말로 적으면 대화 내용에 맞는 스킬이 슬래시 명령 없이 자동으로 뜹니다. "이 글 위키에 저장해줘"는 `ingest`, "내가 X에 대해 뭘 알고 있지?"는 `query`, "이번 세션 마무리하자"는 `crystallize`로 이어집니다. 동작 원리는 [Claude Agent Skills](#claude-agent-skills) 참고.
85
+
78
86
  > 여러 기기 동기화: 위키는 처음부터 git 저장소입니다. remote만 한 번 연결해 두면 이후 매 세션 종료 때 `Stop` 훅이 동기화를 맞춰 줍니다.
79
87
 
80
88
  ---
@@ -138,7 +146,7 @@ Hypomnema ───► 합성 · 마크다운 · git · 훅 · 로컬
138
146
  |---|---|
139
147
  | frontmatter(프런트매터) | 마크다운 페이지 맨 위의 YAML 블록. `title`, `type`, `tags` 같은 항목이 들어갑니다 |
140
148
  | wikilink(위키링크) | `[[페이지-슬러그]]` 형태의 페이지 간 교차 참조. `lint`가 유효성을 확인합니다 |
141
- | ADR | "Architecture Decision Record". 자명하지 않은 설계 결정을 __ 했는지 짧게 적는 마크다운 페이지 |
149
+ | ADR | "Architecture Decision Record". 자명하지 않은 설계 결정을 왜 했는지 짧게 적는 마크다운 페이지 |
142
150
  | schema(스키마) | `SCHEMA.md`에 정의된 타입 분류와 필수 항목 규칙. 페이지가 유효한지 판단하는 기준 |
143
151
  | lint | 읽기 전용 검증기(`hypomnema lint`). 프런트매터·위키링크·스키마를 한꺼번에 점검 |
144
152
  | projection(투영) | 한 방향 자동 파생. `pages/feedback/*.md` → `MEMORY.md`와 CLAUDE.md `<learned_behaviors>` |
@@ -148,7 +156,7 @@ Hypomnema ───► 합성 · 마크다운 · git · 훅 · 로컬
148
156
  | `hot.md` | 프로젝트별 캐시. "방금 무엇을 했는지"(직전 세션의 핵심) |
149
157
  | `session-state.md` | 프로젝트별 캐시. "다음에 무엇을 할지"(다음 세션 시작 때 주입되는 이어받기 데이터) |
150
158
  | `.hypoignore` | 모든 콘텐츠 주입 훅과 `ingest`에서 제외할 경로(글롭 패턴) |
151
- | 관측성 점수(observability score) | 세션별 측정값(ingest·query·session-close·citation 비율). 위키가 실제로 쓰였는지 보여줍니다 |
159
+ | 관측성 점수(observability score) | 세션별 측정값(search·ingest·feedback 활동). 위키가 실제로 쓰였는지 보여줍니다 |
152
160
  | manifest | 설치 스크립트가 쓰는 작은 JSON. 어떤 파일을 어떤 SHA로 설치했는지 기록 |
153
161
  | `additionalContext` | Claude Code 훅이 프롬프트에 컨텍스트를 끼워 넣는 필드. 콘텐츠 주입 훅의 출력 위치 |
154
162
  | 바이트 동일(byte-equal) | `--apply` 전후가 비트 단위로 같은 파일. "건드리지 않았다"의 가장 강한 보장 |
@@ -165,7 +173,7 @@ Hypomnema ───► 합성 · 마크다운 · git · 훅 · 로컬
165
173
 
166
174
  ### 1. 왜 청크 기반 RAG가 아니라 합성인가
167
175
 
168
- RAG는 _낯선_ 코퍼스에 강합니다. 100만 페이지짜리 법률 아카이브를 주면 관련 단편을 잘 찾아냅니다. 그런데 _개인_ 지식의 실패는 정반대 지점에서 옵니다.
176
+ RAG는 낯선 코퍼스에 강합니다. 100만 페이지짜리 법률 아카이브를 주면 관련 단편을 잘 찾아냅니다. 그런데 내가 아는 개인 지식의 실패는 정반대 지점에서 옵니다.
169
177
 
170
178
  - 코퍼스가 작은 대신 중복이 많습니다(같은 주제의 글 3편).
171
179
  - 사용자는 단편이 아니라 관점을 원합니다.
@@ -175,7 +183,7 @@ Hypomnema는 청크가 아니라 페이지를 지식 단위로 봅니다. 새
175
183
 
176
184
  ### 2. 왜 독자 포맷이 아니라 마크다운 + git인가
177
185
 
178
- 개인 지식 베이스는 특정 도구 하나보다 오래 살아남아야 합니다. 마크다운은 살아남습니다. git도 살아남습니다. 둘 다 어떤 LLM이든 읽고 오프라인에서 동작합니다. 30년치 도구 생태계가 받쳐 줍니다. 스택은 일부러 _지루하게_ 골랐습니다. 흥미로운 부분은 _그 위에서 Claude가 무엇을 하느냐_니까요.
186
+ 개인 지식 베이스는 특정 도구 하나보다 오래 살아남아야 합니다. 마크다운은 살아남습니다. git도 살아남습니다. 둘 다 어떤 LLM이든 읽고 오프라인에서 동작합니다. 30년치 도구 생태계가 받쳐 줍니다. 스택은 일부러 지루하게 골랐습니다. 흥미로운 부분은 그 위에서 Claude가 무엇을 하느냐에 있으니까요.
179
187
 
180
188
  ### 3. 왜 수동 명령이 아니라 라이프사이클 훅인가
181
189
 
@@ -189,7 +197,7 @@ Hypomnema는 청크가 아니라 페이지를 지식 단위로 봅니다. 새
189
197
  | `PostToolUse` (Write/Edit) | `git add` |
190
198
  | `Stop` | `git pull --rebase && git commit && git push` |
191
199
 
192
- 설치하고 나면 위키를 _관리_하는 일을 그만두게 됩니다. 그냥 쌓입니다.
200
+ 설치하고 나면 위키를 관리하는 일을 그만두게 됩니다. 그냥 쌓입니다.
193
201
 
194
202
  ### 4. 왜 재개에 `hot.md` 캐시를 쓰는가
195
203
 
@@ -197,7 +205,7 @@ Hypomnema는 청크가 아니라 페이지를 지식 단위로 봅니다. 새
197
205
 
198
206
  ### 5. 왜 feedback → behavior 파이프라인인가
199
207
 
200
- 대부분의 AI 도구는 교정을 _지금 대화에 한해서만_ 받아들입니다. 다음으로 이어지지 않습니다. Hypomnema는 모든 `/hypo:feedback`을 `pages/feedback/`으로 흘려보냅니다. 오래 갈 규칙은 `CLAUDE.md`의 `<learned_behaviors>` 블록으로 승격합니다. 이후 모든 세션, 위키를 pull하는 모든 기기에서 살아 있습니다.
208
+ 대부분의 AI 도구는 교정을 지금 대화에 한해서만 받아들입니다. 다음으로 이어지지 않습니다. Hypomnema는 모든 `/hypo:feedback`을 `pages/feedback/`으로 흘려보냅니다. 오래 갈 규칙은 `CLAUDE.md`의 `<learned_behaviors>` 블록으로 승격합니다. 이후 모든 세션, 위키를 pull하는 모든 기기에서 살아 있습니다.
201
209
 
202
210
  ### 6. 왜 API 키도, 벡터 DB도, 외부 서비스도 없는가
203
211
 
@@ -205,7 +213,7 @@ Hypomnema는 청크가 아니라 페이지를 지식 단위로 봅니다. 새
205
213
 
206
214
  ### 7. 왜 privacy mode 플래그가 아니라 `.hypoignore`인가
207
215
 
208
- v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실과 부딪히자 바로 무너졌습니다. 모든 privacy 결정은 결국 _경로 단위_ 질문이었고, 그건 단일 파일(`.hypoignore`) 하나로 처리됩니다. v1.1에서 mode 개념을 통째로 지웠습니다. 파일 하나가 모든 결정을 담는 단일 원천 구조입니다.
216
+ v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실과 부딪히자 바로 무너졌습니다. 모든 privacy 결정은 결국 경로 단위 질문이었고, 그건 단일 파일(`.hypoignore`) 하나로 처리됩니다. v1.1에서 mode 개념을 통째로 지웠습니다. 파일 하나가 모든 결정을 담는 단일 원천 구조입니다.
209
217
 
210
218
  ---
211
219
 
@@ -217,9 +225,9 @@ v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실
217
225
 
218
226
  | 명령어 | 하는 일 | 언제 쓰나 |
219
227
  |---|---|---|
220
- | `/hypo:ingest` | 원본을 `sources/`에 보관하고 Claude가 `pages/`에 구조화된 페이지를 합성. 셸 헬퍼(`scripts/ingest.mjs`)는 읽기 전용이라 아직 인제스트 안 된 소스를 _목록만_ 출력 | 보관할 가치가 있는 글을 읽었을 때 |
228
+ | `/hypo:ingest` | 원본을 `sources/`에 보관하고 Claude가 `pages/`에 구조화된 페이지를 합성. 셸 헬퍼(`scripts/ingest.mjs`)는 읽기 전용이라 아직 인제스트 안 된 소스를 목록만 출력 | 보관할 가치가 있는 글을 읽었을 때 |
221
229
  | `/hypo:query` | BM25 검색 + LLM 합성 + `[[wikilink]]` 인용 | 자기 노트에 근거한 답이 필요할 때 |
222
- | `/hypo:crystallize` | 세션 마무리 체크리스트(1~6단계) 실행. 요청하면 초안 합성(7~11단계)까지 | 단순하지 않은 세션을 마칠 때 |
230
+ | `/hypo:crystallize` | 세션 마무리 체크리스트(1-6단계) 실행. 요청하면 초안 합성(7-11단계)까지 | 단순하지 않은 세션을 마칠 때 |
223
231
  | `/hypo:resume` | 활성 프로젝트의 가장 최근 세션 상태 불러오기 | 잠시 미뤄둔 프로젝트로 돌아올 때 |
224
232
  | `/hypo:feedback` | AI 행동 교정을 기록. 영구 규칙 승격 후보가 됨 | Claude가 잘못했을 때, 또는 반대로 잘했을 때 |
225
233
  | `/hypo:verify` | `verify_by` 프런트매터가 붙은 페이지를 점검 | 시간이 지나 옛 정보가 됐을 수 있을 때 |
@@ -248,8 +256,6 @@ v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실
248
256
 
249
257
  모든 훅은 위키 루트를 `HYPO_DIR` 환경변수 → `hypo-config.md` 스캔 → `~/hypomnema` 기본값 순으로 찾고, `hypo-shared.mjs`(`hooks.json`의 `shared` 필드로 선언)를 공유합니다.
250
258
 
251
- 이와 별도로 `SessionStart` 훅은 npm 레지스트리와 Claude Code 플러그인 마켓플레이스를 백그라운드에서 확인합니다(세션 시작을 막지 않습니다). 새 버전이 올라와 있으면 다음 세션 시작 때 "Update available!" 안내가 한 줄 뜹니다. `HYPO_NO_UPDATE_CHECK=1`, `NO_UPDATE_NOTIFIER=1`을 주거나 `CI=true` 환경이면 건너뜁니다.
252
-
253
259
  ### 셋업 & 유지보수
254
260
 
255
261
  | 명령어 | 목적 |
@@ -261,9 +267,20 @@ v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실
261
267
  | `/hypo:stats` | 위키 통계 |
262
268
  | `/hypo:audit` | observability 감사 (세션별 메트릭, 주간 보고서) |
263
269
 
270
+ > 업데이트 알림: `SessionStart` 훅이 npm 레지스트리와 Claude Code 플러그인 마켓플레이스를 백그라운드에서 확인합니다(세션 시작을 막지 않습니다). 새 버전이 올라와 있으면 다음 세션 시작 때 "Update available!" 안내가 한 줄 뜹니다. `HYPO_NO_UPDATE_CHECK=1`, `NO_UPDATE_NOTIFIER=1`을 주거나 `CI=true` 환경이면 건너뜁니다.
271
+
264
272
  ### Claude Agent Skills
265
273
 
266
- 합성이 핵심인 명령어(`ingest`, `query`, `crystallize`, `lint`, `verify`, `graph`)는 `skills/<name>/SKILL.md`로도 등록돼 있습니다. 대화 내용이 해당 스킬의 `description`과 맞으면 Claude Agent Skills 메커니즘이 슬래시 명령 없이도 자동으로 호출합니다.
274
+ 합성이 핵심인 명령어(`ingest`, `query`, `crystallize`, `lint`, `verify`, `graph`)는 `skills/<name>/SKILL.md`로도 등록돼 있습니다. 대화 내용이 해당 스킬의 `description`과 맞으면 Claude Agent Skills 메커니즘이 슬래시 명령 없이도 자동으로 호출합니다. 즉 명령어를 정확히 몰라도 하려는 일을 말로 적으면 됩니다.
275
+
276
+ | 이렇게 말하면 | 트리거되는 스킬 |
277
+ |---|---|
278
+ | "이 링크(글) 위키에 저장해줘" | `ingest` |
279
+ | "내가 X에 대해 뭘 알고 있지?", "예전에 내린 결정 찾아줘" | `query` |
280
+ | "이번 세션 마무리하자", "오늘 작업 정리해줘" | `crystallize` |
281
+ | "이 페이지들 링크 안 깨졌는지 확인해줘" | `lint` |
282
+
283
+ 슬래시 명령은 트리거가 확실할 때, 자연어는 흐름을 끊고 싶지 않을 때 쓰면 됩니다. 둘은 같은 스킬로 이어집니다.
267
284
 
268
285
  ---
269
286
 
@@ -302,7 +319,7 @@ E. 멈춘 프로젝트 재개.
302
319
  - 자격증명·토큰·비밀. `.hypoignore`로 민감 경로를 제외하세요
303
320
  - 이번 세션의 일시적 작업 목록. 대화 안의 작업 목록을 쓰세요
304
321
  - 레포에서 도출되는 코드 패턴. `git log`, `grep`이 원본 기준입니다
305
- - 관리 주체가 따로 있는 정보 (Jira, Confluence, API 문서). 미러가 아니라 _합성본_만 인제스트하세요
322
+ - 관리 주체가 따로 있는 정보 (Jira, Confluence, API 문서). 미러가 아니라 합성한 결과만 인제스트하세요
306
323
 
307
324
  ---
308
325
 
@@ -345,9 +362,9 @@ E. 멈춘 프로젝트 재개.
345
362
 
346
363
  `.hypoignore`는 훅이 무시할 경로를 정합니다 (기본: `*.pdf`, `*.zip`, `*.pem`, `*.env` 등). 직접 편집하면 됩니다. privacy mode 플래그는 없습니다. 파일 하나가 모든 결정을 담는 단일 원천 구조입니다.
347
364
 
348
- > 모델 사업자에게 전송되는 범위: 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` 패턴을 먼저 점검하세요.
365
+ > 모델 사업자에게 전송되는 범위: 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` 패턴을 먼저 점검하세요.
349
366
 
350
- > 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/` 콘텐츠는 일부러 관리하지 않습니다. 위키를 거치지 않는 기타 agent/skill, 머신 고유 `settings.local.json` 같은 일반 Claude Code 설정의 기기 간 동기화는 [chezmoi](https://www.chezmoi.io/) 같은 별도 dotfiles 매니저를 권합니다.
367
+ > 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/` 콘텐츠는 일부러 관리하지 않습니다. 위키를 거치지 않는 기타 agent/skill, 머신 고유 `settings.local.json` 같은 일반 Claude Code 설정의 기기 간 동기화는 [chezmoi](https://www.chezmoi.io/) 같은 별도 dotfiles 매니저를 권합니다.
351
368
 
352
369
  ### `/hypo:*` 커맨드는 어디서 오는가
353
370
 
package/README.md CHANGED
@@ -15,7 +15,7 @@ English | [한국어](README.ko.md)
15
15
 
16
16
  LLM-native personal wiki for Claude Code. Knowledge that compounds.
17
17
 
18
- _Make Claude take notes, and measure whether it actually does._
18
+ Make Claude take notes, and measure whether it actually does.
19
19
 
20
20
  [Quick Start](#quick-start) • [How It Compares](#how-it-compares) • [Design Decisions](#design-decisions) • [Features](#features) • [Architecture](docs/ARCHITECTURE.md) • [Contributing](docs/CONTRIBUTING.md)
21
21
 
@@ -25,14 +25,20 @@ New to the terms below? Keep the [Term decoder](#term-decoder) open in another t
25
25
 
26
26
  ### Where automation stands today
27
27
 
28
- The current release is v1.4.2, which hardened the session-close path: a close is attributed to the right project, an unregistered tag or a hand-written `log.md` entry no longer false-blocks it, and the recovery command it prints is runnable in a plugin install. Wiki work (ingest, query, session-close) still runs on explicit `/hypo:*` commands. The v2 goal is full autonomy: Claude reading, writing, and synthesizing the wiki without being asked, which is the direction this is heading. v1.1.0 shipped the observability score that measures how often the wiki is actually used per session. v1.2.0 added four autonomous lanes on top:
28
+ The current release is v1.5.1. It sharpens how the wiki signals its own freshness: a page whose `verify_by_date` has passed is flagged `[STALE ...]` at the moment it is injected, so a dated answer is visible instead of trusted silently, and lookup usage is tracked locally so crystallize can surface a linked-but-never-injected page as a cold candidate. The same release makes session close robust to the standard `session | project: title` colon log format that had been misread as stale. The v1.5.0 line turned to cross-machine reliability and everyday friction before it: `cwd`-first resume on a git-synced vault, a code-repo session told where the wiki lives, and session close no longer re-listing unrelated lint debt.
29
+
30
+ Wiki work (ingest, query, session-close) still starts from an explicit `/hypo:*` command or plain language. The v2 goal is full autonomy: Claude reading, writing, and synthesizing the wiki without being asked, which is the direction this is heading.
31
+
32
+ The lanes that already run on their own: v1.1.0 shipped the observability score that measures how often the wiki is used per session, and v1.2.0 added four automated areas on top.
29
33
 
30
34
  - Edit feedback in one place and the rest follows. `pages/feedback/` is the single source of truth for behavior corrections, and Hypomnema derives `MEMORY.md` and the `<learned_behaviors>` block inside `~/.claude/CLAUDE.md` from it automatically.
31
35
  - Extensions sync alongside. Anything under `~/hypomnema/extensions/{agents,commands,hooks,skills}/` is mirrored into `~/.claude/` automatically. The `--codex` flag also mirrors `hooks` and `commands` into `~/.codex/`; `agents` and `skills` are Claude-only and skipped on purpose.
32
36
  - Auto-project creation. 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.
33
37
  - Session-close cleanup and `/clear` recovery. After a non-trivial session, a "save a minimal session-close note?" prompt appears automatically; a `/clear` after a forgotten close is detected and recovered at the next session start.
34
38
 
35
- What changed per version lives in the [CHANGELOG](CHANGELOG.md). One policy worth knowing up front: `hypomnema upgrade --apply` never overwrites a `SCHEMA.md` you have edited. When the schema bumps, upgrade writes a migration report into the wiki root and leaves the actual changes for you to apply (the policy the code calls _Option C_).
39
+ What changed per version lives in the [CHANGELOG](CHANGELOG.md).
40
+
41
+ One upgrade policy worth knowing up front: `hypomnema upgrade --apply` never overwrites a `SCHEMA.md` you have edited. When the schema bumps, upgrade writes a migration report into the wiki root and leaves the actual changes for you to apply (the policy the code calls Option C).
36
42
 
37
43
  ---
38
44
 
@@ -75,6 +81,8 @@ hypomnema
75
81
 
76
82
  Hooks handle the rest: auto-staging, auto-commit/push, session-state injection, and lookup signals.
77
83
 
84
+ > You don't have to memorize the commands. Describe what you want in plain language and the matching skill fires with no slash command: "save this article to the wiki" triggers `ingest`, "what do I know about X?" triggers `query`, "let's wrap up this session" triggers `crystallize`. See [Claude Agent Skills](#claude-agent-skills) for how it works.
85
+
78
86
  > Sync across machines: the wiki is already a git repo. Add a remote, push once, and the `Stop` hook keeps every machine in sync afterwards.
79
87
 
80
88
  ---
@@ -148,7 +156,7 @@ These are the recurring terms used in the rest of the README. Keep this table op
148
156
  | `hot.md` | Per-project cache: "what just happened" (most recent session highlights) |
149
157
  | `session-state.md` | Per-project cache: "what's next" (the resume payload for the next session) |
150
158
  | `.hypoignore` | Glob patterns that exclude paths from every content-injection hook and from `ingest` |
151
- | observability score | A per-session metric (ingest / query / session-close / citation rates) that measures whether the wiki is actually being used |
159
+ | observability score | A per-session metric (search / ingest / feedback activity) that measures whether the wiki is actually being used |
152
160
  | manifest | A small JSON the install scripts write to track exactly which files were installed and at what SHA |
153
161
  | `additionalContext` | The Claude Code hook field that injects extra context into the prompt: where content-injection hooks emit |
154
162
  | byte-equal | A file that comes out of `--apply` bit-for-bit identical to before: the strongest "we did not touch this" guarantee |
@@ -219,7 +227,7 @@ Nine commands cover the full capture → retrieval → consolidation cycle.
219
227
  |---|---|---|
220
228
  | `/hypo:ingest` | Saves the raw source under `sources/`; Claude synthesizes a structured page under `pages/`. The shell helper (`scripts/ingest.mjs`) is read-only and only _lists_ pending sources | Anytime you read something worth keeping |
221
229
  | `/hypo:query` | BM25 retrieval + LLM synthesis with `[[wikilink]]` citations | When you need an answer grounded in your own notes |
222
- | `/hypo:crystallize` | Runs the session-close checklist (steps 1~6) and, on request, synthesizes drafts (steps 7~11) | End of a non-trivial session |
230
+ | `/hypo:crystallize` | Runs the session-close checklist (steps 1-6) and, on request, synthesizes drafts (steps 7-11) | End of a non-trivial session |
223
231
  | `/hypo:resume` | Loads the most recent session state for an active project | Coming back to a paused project |
224
232
  | `/hypo:feedback` | Records an AI behavior correction; eligible for promotion to permanent rules | When Claude gets something wrong, or gets it exactly right |
225
233
  | `/hypo:verify` | Audits pages with `verify_by` frontmatter | When time-bound knowledge might have aged out |
@@ -248,8 +256,6 @@ Nine commands cover the full capture → retrieval → consolidation cycle.
248
256
 
249
257
  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).
250
258
 
251
- The `SessionStart` hook also runs a non-blocking background check against npm and the Claude Code plugin marketplace, and prints an "Update available!" banner the next time a newer version has been published. Opt out with `HYPO_NO_UPDATE_CHECK=1`, `NO_UPDATE_NOTIFIER=1`, or by running under `CI=true`.
252
-
253
259
  ### Setup & maintenance
254
260
 
255
261
  | Command | Purpose |
@@ -261,9 +267,20 @@ The `SessionStart` hook also runs a non-blocking background check against npm an
261
267
  | `/hypo:stats` | Wiki statistics |
262
268
  | `/hypo:audit` | Observability audit (per-session metrics, weekly report) |
263
269
 
270
+ > Update notice: the `SessionStart` hook runs a non-blocking background check against npm and the Claude Code plugin marketplace, and prints an "Update available!" banner the next time a newer version has been published. Opt out with `HYPO_NO_UPDATE_CHECK=1`, `NO_UPDATE_NOTIFIER=1`, or by running under `CI=true`.
271
+
264
272
  ### Claude Agent Skills
265
273
 
266
- The synthesis-heavy commands (`ingest`, `query`, `crystallize`, `lint`, `verify`, `graph`) are also exposed as Claude Agent Skills in `skills/<name>/SKILL.md`, so they auto-trigger when the conversation matches their description, with no slash command required.
274
+ The synthesis-heavy commands (`ingest`, `query`, `crystallize`, `lint`, `verify`, `graph`) are also exposed as Claude Agent Skills in `skills/<name>/SKILL.md`, so they auto-trigger when the conversation matches their description, with no slash command required. You don't need to know the exact command; just say what you want.
275
+
276
+ | Say this | Skill it triggers |
277
+ |---|---|
278
+ | "save this link/article to the wiki" | `ingest` |
279
+ | "what do I know about X?", "find that decision I made" | `query` |
280
+ | "let's wrap up this session", "save what we did today" | `crystallize` |
281
+ | "check these pages for broken links" | `lint` |
282
+
283
+ Use the slash command when you want to be explicit, plain language when you don't want to break your flow. Both reach the same skill.
267
284
 
268
285
  ---
269
286
 
package/commands/audit.md CHANGED
@@ -2,7 +2,7 @@
2
2
  description: Audit recent sessions for observability gaps and optionally write the weekly report. Use when the user asks to review session quality, find tracking gaps, or generate the weekly summary.
3
3
  ---
4
4
 
5
- You are running `/hypo:audit`. Inspect recent Claude Code sessions to see how much of the wiki's value motion is actually happening (search, ingest, citation, feedback), then either show the result inline or write a weekly observability page.
5
+ You are running `/hypo:audit`. Inspect recent Claude Code sessions to see how much of the wiki's value motion is actually happening (search, ingest, feedback), then either show the result inline or write a weekly observability page.
6
6
 
7
7
  ## What this shows
8
8
 
@@ -16,7 +16,7 @@ Definition: [[pages/observability/_index]].
16
16
 
17
17
  ## Step 1 — Run script
18
18
 
19
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
19
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
20
20
 
21
21
  If the user specified a Hypomnema directory, pass it as `--hypo-dir="<path>"`. Otherwise omit the flag.
22
22
 
@@ -17,7 +17,7 @@ If `/hypo:crystallize` was invoked to close a session (via an explicit close sig
17
17
 
18
18
  ## Step 1a — Session-close advisory reflections
19
19
 
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.
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
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.
@@ -30,9 +30,9 @@ 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**. 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 the `--apply-session-close` apply path (the Step 3 command), which performs idempotent atomic writes and gates the result with lint.
34
34
 
35
- Payload shape (`project` + 5 content fields required, 1 conditional, per Spec §5.2.7 / §8.3 + ADR 0029):
35
+ Payload shape (`project` + 4 content fields required; `log` optional/derived; `openQuestions` conditional, per Spec §5.2.7 / §8.3):
36
36
 
37
37
  ```json
38
38
  {
@@ -54,7 +54,8 @@ Field rules:
54
54
  - `project`: **required**. Slug of the project being closed (matches a `projects/<slug>/` directory). Must be a single path segment, charset `A-Za-z0-9._-`, with at least one alphanumeric and not a dot-only name (`.`, `..`, `...`). Apply never infers the target from recency; a same-date pointer-table tie could otherwise write the close into the wrong project (B-3). A missing, malformed, or non-existent value fails the apply before any write.
55
55
  - `date` — optional. Defaults to today (local). Must be `YYYY-MM-DD` if supplied.
56
56
  - `openQuestions` — optional. Include only when `pages/open-questions.md` exists and changed this session.
57
- - All other top-level fields are required.
57
+ - `log`: optional. Omit it by default (apply derives the canonical `## [date] session | <project>` line from your `sessionLog` heading). Supply it only for a custom log.md line, which must still be a canonical `session | <project>` heading, or the apply fails at `stage='pre-apply-verification'`.
58
+ - The remaining fields (`sessionState`, `projectHot`, `rootHot`, `sessionLog`) are required.
58
59
 
59
60
  Notes:
60
61
 
@@ -68,14 +69,14 @@ Content guidance for each slot:
68
69
  2. **projectHot** — session snapshot under 500 words: what changed and decisions made. Do **not** put next-step tasks here; those belong in `sessionState`.
69
70
  3. **rootHot** — active-projects pointer table with this project's `Last Session` date set to today.
70
71
  4. **sessionLog** — one session entry to append to `projects/<name>/session-log/YYYY-MM-DD.md` (daily shard).
71
- 5. **log** one `session` entry to append to `<hypo-root>/log.md`.
72
+ 5. **log** (optional): a custom `session` entry for `<hypo-root>/log.md`. Omit it to let apply derive the canonical line from `sessionLog`.
72
73
  6. **openQuestions** (conditional) — only if `pages/open-questions.md` exists and questions were raised or resolved this session.
73
74
 
74
75
  ---
75
76
 
76
77
  ## Step 3 — Apply the payload
77
78
 
78
- The script paths in this command resolve via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run them as written. If one appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
79
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
79
80
 
80
81
  ```bash
81
82
  node ${CLAUDE_PLUGIN_ROOT}/scripts/crystallize.mjs \
@@ -96,7 +97,9 @@ so it stops re-prompting. Omit it only when running crystallize purely for
96
97
  synthesis (no session-close intent) — the marker is then simply not written.
97
98
 
98
99
  > **Source rule for `--session-id`:** use only the main conversation's session id
99
- > (the id shown in the `[WIKI_AUTOCLOSE]` block reason, or `$CLAUDE_SESSION_ID`).
100
+ > (the id shown in the `[WIKI_AUTOCLOSE]` block reason, or the injected
101
+ > `$CLAUDE_CODE_SESSION_ID`; accept the legacy spelling via
102
+ > `${CLAUDE_CODE_SESSION_ID:-$CLAUDE_SESSION_ID}`).
100
103
  > Do NOT extract it from a background task output path or Agent thread (e.g.,
101
104
  > `/tmp/.../<uuid>/tasks/...`). A UUID from such a path is a background task id,
102
105
  > not the main conversation id. Passing it causes `markerSkipReason:
@@ -114,12 +117,12 @@ synthesis (no session-close intent) — the marker is then simply not written.
114
117
 
115
118
  **Two lint gates run automatically, scoped to the files this close writes:**
116
119
 
117
- 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.
120
+ Both gates judge only the **payload files** (the 5 mandatory close files + `open-questions.md`). Lint debt this close did not author is never gated so an unrelated broken page elsewhere cannot block your close. It is reported as a non-blocking notice, scoped to the close-target project: debt under `projects/<project>/` is listed by file in `notices[]`; debt elsewhere (other projects, shared `pages/`, root files) folds into the `otherDebtCount` integer so the same untouched-file debt does not re-list its filenames on every close (run `/hypo:lint` for the full list).
118
121
 
119
- 1. **Preflight** — `lint.mjs --json` runs **before** any payload bytes are written. Errors in overwrite targets (sessionState / projectHot / rootHot / openQuestions) are filtered (about to be replaced). Errors in an **append target** (session-log / log.md) still block (appending can't repair existing corruption) → exit 1 with `stage='preflight-lint'`. Errors outside the payload files → `notices[]`, apply proceeds.
122
+ 1. **Preflight** — an internal `lint --json` preflight runs **before** any payload bytes are written. Errors in overwrite targets (sessionState / projectHot / rootHot / openQuestions) are filtered (about to be replaced). Errors in an **append target** (session-log / log.md) still block (appending can't repair existing corruption) → exit 1 with `stage='preflight-lint'`. Errors outside the payload files → `notices[]`, apply proceeds.
120
123
  2. **Post-apply** — lint re-runs after the writes. Blocks only on **errors** in payload files (a payload-introduced malformed body / bad frontmatter); pre-existing errors elsewhere → `notices[]`. A lint crash (unparseable output) always blocks. Broken wikilinks are lint **warnings** (W4 — forward references to planned pages are normal) and are not gated here. Surfaces as `stage='post-apply-lint'` (or `'post-apply-verification+lint'` if freshness also fails).
121
124
 
122
- > **Manual close (direct Write tool calls)** clears the Stop-chain block via `--mark-session-closed --session-id=<id>`. Both marker writers apply a **user-close hard gate** (ADR 0055): the marker is written only when the session's transcript carries a genuine user close signal — an NL close phrase, a `/compact`, or an accepted AskUserQuestion [세션 마무리] answer. The transcript is resolved **strictly from `--session-id`** (a globally-unique id, globbed under `~/.claude/projects/`), never from a CLI arg, so a model that runs the writer on its own — without the user ever signalling close — is refused, and a forged path cannot point the gate at someone else's close-intent. The lint scope is widened from that same resolved transcript. `--transcript-path` is **not** consulted by the marker gate; it survives only to scope `--check-session-close`'s lint (which writes no marker).
125
+ > **Manual close (direct Write tool calls)** clears the Stop-chain block via `--mark-session-closed --session-id=<id>`. Both marker writers apply a **user-close hard gate**: the marker is written only when the session's transcript carries a genuine user close signal — an NL close phrase, a `/compact`, or an accepted AskUserQuestion [세션 마무리] answer. The transcript is resolved **strictly from `--session-id`** (a globally-unique id, globbed under `~/.claude/projects/`), never from a CLI arg, so a model that runs the writer on its own — without the user ever signalling close — is refused, and a forged path cannot point the gate at someone else's close-intent. The lint scope is widened from that same resolved transcript. `--transcript-path` is **not** consulted by the marker gate; it survives only to scope `--check-session-close`'s lint (which writes no marker).
123
126
 
124
127
  ---
125
128
 
@@ -129,7 +132,8 @@ The result JSON includes a `stage` field when `ok: false`. Branch on it:
129
132
 
130
133
  | `stage` | What broke | How to recover |
131
134
  |---|---|---|
132
- | `preflight-lint` | A payload file (append target session-log / log.md) has a pre-existing blocking lint error. | Fix the lint error in that file, then re-run. No payload bytes were written. (Debt outside the payload files is a non-blocking notice, not this stage.) |
135
+ | `pre-apply-verification` | A payload heading does not match the freshness contract the close gate enforces: `sessionLog.entry` has no dated `## [YYYY-MM-DD] …` heading, or an explicit `log.entry` is not the canonical `## [date] session | <project>` line. Caught **before** any write. | Fix the heading in the payload (the session-log entry needs a bracketed dated heading; the log line needs `session \| <project>` after the date, colon or space delimiter), then re-run. No payload bytes were written. |
136
+ | `preflight-lint` | A payload file (append target: session-log / log.md) has a pre-existing blocking lint error. | Fix the lint error in that file, then re-run. No payload bytes were written. (Debt outside the payload files is a non-blocking notice, not this stage.) |
133
137
  | `post-apply-verification` | A mandatory file's `updated:` frontmatter is stale (≠ today) after apply. | Edit the payload's stale `content` (or supply correct `date`), then re-run. Writes are idempotent — re-applying a corrected payload is safe. |
134
138
  | `post-apply-lint` | The payload introduced an error-level lint blocker in a payload file (malformed body / bad frontmatter), or lint crashed. | Fix the offending content in the payload, then re-run. (Broken wikilinks are W4 warnings — not gated.) |
135
139
  | `post-apply-verification+lint` | Both above. | Fix both; re-run. |
@@ -142,11 +146,15 @@ Once `ok: true`, report:
142
146
  - ✓ open-questions applied (or skipped if unchanged)
143
147
  - ✓ log.md entry appended
144
148
  - ✓ post-apply lint clean
145
- - **marker written?** (required check): if `markerWritten: true`, report "session-close marker written"; if `markerWritten: false`, report "session-close marker NOT written (reason: `<markerSkipReason>`)" and do NOT declare the session "closed" or "complete". A missing marker means the Stop-chain is still open. Instruct the user to re-run with the correct main-conversation `--session-id`.
149
+ - **marker written?** (required check): if `markerWritten: true`, report "session-close marker written"; if `markerWritten: false`, report "session-close marker NOT written (reason: `<markerSkipReason>`)" and do NOT declare the session "closed" or "complete". A missing marker means the Stop-chain is still open; recover per the `markerSkipReason` branch below.
146
150
 
147
151
  If `markerWritten: true` (or `--session-id` was not passed): ask: "Session closed. Would you like to also run knowledge synthesis now, or stop here?"
148
152
 
149
- If `markerWritten: false`: do NOT say "session closed." Say instead: "Files applied and verified (ok: true), but the session-close marker was not written (reason: `<markerSkipReason>`). The Stop-chain is still active. To fully close: re-run with the correct main-conversation `--session-id`."
153
+ If `markerWritten: false`, do NOT say "session closed." Branch on `markerSkipReason`:
154
+
155
+ - `no-user-close-signal`: the files applied cleanly and the `--session-id` resolved a transcript, but that transcript carries no close phrase the gate recognizes (the user asked to close in wording that fell outside the close-signal set, e.g. "세션 마무리까지 진행해줘" or "세션 마무리 진행"). Re-running with the same id will not help, since the transcript is unchanged. Instead, confirm intent once with `AskUserQuestion`, header "세션", a single option labelled **세션 마무리** (설명: "이 세션을 마무리하고 close 마커를 기록"). If the user picks 세션 마무리, that answer becomes a recognized close signal in the transcript, so re-run the exact same `--apply-session-close … --session-id` command (the writes are idempotent no-ops; the marker now lands). If the user declines, leave the session unmarked: the wiki record stands, but the session is not closed. Do NOT touch the close-signal matcher itself.
156
+ - `transcript-unresolved` (or a background / agent id was passed): say "Files applied and verified (ok: true), but the session-close marker was not written (reason: `<markerSkipReason>`). The Stop-chain is still active. To fully close: re-run with the correct main-conversation `--session-id`."
157
+ - any other reason (`compact-gate-not-ok`, `commit-failed: …`, `marker-did-not-land`): surface the reason verbatim and address it (resolve the compact blocker, fix the git / disk issue) before re-running.
150
158
 
151
159
  If the user says stop, end here. Otherwise continue to Step 5.
152
160
 
@@ -154,7 +162,7 @@ If the user says stop, end here. Otherwise continue to Step 5.
154
162
 
155
163
  ## Step 5 — Surface synthesis candidates
156
164
 
157
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
165
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
158
166
 
159
167
  ```bash
160
168
  node ${CLAUDE_PLUGIN_ROOT}/scripts/crystallize.mjs [--hypo-dir="<path>"] [--min-group=2]
@@ -18,7 +18,7 @@ You are running `/hypo:doctor`. Verify the health of the current Hypomnema wiki
18
18
 
19
19
  ## Step 1 — Resolve Hypomnema directory
20
20
 
21
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
21
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
22
22
 
23
23
  If the user specified a Hypomnema directory, pass it as `--hypo-dir="<path>"`. Otherwise omit the flag and the script resolves the Hypomnema root automatically: `HYPO_DIR` env → `hypo-config.md` scan → `~/hypomnema` default.
24
24
 
@@ -2,7 +2,7 @@
2
2
  description: Record an AI behavior correction or preference into the wiki. Use when the user corrects how you work or states a lasting preference to remember.
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).
5
+ You are running `/hypo:feedback`. Capture a behavior correction or preference into `pages/feedback/` — the **single source of truth** for learned behaviors.
6
6
 
7
7
  ## What this does
8
8
 
@@ -36,7 +36,7 @@ If **claude-learned** is among the targets, the page must be `scope: global` + `
36
36
 
37
37
  ## Step 2 — List existing feedback (optional)
38
38
 
39
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
39
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
40
40
 
41
41
  To check for an existing topic, run:
42
42
 
package/commands/graph.md CHANGED
@@ -14,7 +14,7 @@ You are running `/hypo:graph`. Generate a link dependency graph from wiki pages.
14
14
 
15
15
  ## Step 1 — Run script
16
16
 
17
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
17
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
18
18
 
19
19
  If the user specified a Hypomnema directory, pass it as `--hypo-dir="<path>"`. Otherwise omit the flag.
20
20
 
@@ -27,7 +27,7 @@ Do **not** fetch the URL or read the file yet — the privacy guard in Step 2 mu
27
27
 
28
28
  ## Step 2 — Privacy guard (`.hypoignore`)
29
29
 
30
- The script paths below resolve via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run them as written. If one appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
30
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
31
31
 
32
32
  Refuse to ingest secrets (`.env`, SSH keys, credentials) before they ever reach `sources/`. Run the guard for **both** the input path and the destination path:
33
33
 
package/commands/init.md CHANGED
@@ -43,7 +43,7 @@ Ask the following questions **one at a time**. Use the default if the user press
43
43
 
44
44
  ## Step 2a — From Remote (skip wizard) {#step-2a}
45
45
 
46
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
46
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
47
47
 
48
48
  If the user provided `--from-remote <url>`, run:
49
49
 
@@ -64,7 +64,7 @@ node ${CLAUDE_PLUGIN_ROOT}/scripts/init.mjs \
64
64
 
65
65
  ## Step 2 — Run the init script (new wiki)
66
66
 
67
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
67
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
68
68
  Then run:
69
69
 
70
70
  ```bash
package/commands/lint.md CHANGED
@@ -16,7 +16,7 @@ You are running `/hypo:lint`. Validate all wiki pages for frontmatter correctnes
16
16
 
17
17
  ## Step 1 — Run script
18
18
 
19
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
19
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
20
20
 
21
21
  If the user specified a Hypomnema directory, pass it as `--hypo-dir="<path>"`. Otherwise omit the flag and the script resolves the Hypomnema root automatically via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
22
22
 
package/commands/query.md CHANGED
@@ -20,7 +20,7 @@ Ask the user what they want to know if it was not provided in the command invoca
20
20
 
21
21
  ## Step 2 — Search
22
22
 
23
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
23
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
24
24
 
25
25
  Run full-text search:
26
26
 
@@ -16,7 +16,7 @@ You are running `/hypo:rename`. Move a page or directory and content-aware rewri
16
16
 
17
17
  ## Step 1 — Run script
18
18
 
19
- The script path below resolves via `${CLAUDE_PLUGIN_ROOT}`, which the plugin harness expands to this package's absolute path before you see it, so run it as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
19
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
20
20
 
21
21
  If the user specified a Hypomnema directory, pass it as `--hypo-dir="<path>"`. Otherwise omit the flag.
22
22
 
@@ -20,7 +20,7 @@ If the user named a project in the command invocation, use that. Otherwise, run:
20
20
  node ${CLAUDE_PLUGIN_ROOT}/scripts/resume.mjs [--hypo-dir="<path>"] [--project=<name>]
21
21
  ```
22
22
 
23
- The plugin harness expands `${CLAUDE_PLUGIN_ROOT}` to this package's absolute path before you see it, so run the command as written. If it appears unexpanded (a literal `${CLAUDE_PLUGIN_ROOT}`), read the package root from the `hypo@hypomnema` installPath in `~/.claude/plugins/installed_plugins.json` rather than guessing from the cache layout.
23
+ Bundled scripts here run via `${CLAUDE_PLUGIN_ROOT}/scripts/`. To resolve that package root: if `${CLAUDE_PLUGIN_ROOT}` is already an absolute path, use it; otherwise read `pkgRoot` from `~/.claude/hypo-pkg.json` (only when non-empty and the target script exists under it); otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the user to run `hypomnema upgrade --apply` or reinstall instead of guessing the cache layout.
24
24
 
25
25
  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`.
26
26