hypomnema 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.ko.md +4 -2
- package/README.md +4 -2
- package/commands/audit.md +2 -2
- package/commands/crystallize.md +113 -23
- package/commands/feedback.md +40 -26
- package/commands/ingest.md +31 -9
- package/commands/upgrade.md +2 -2
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CONTRIBUTING.md +1 -1
- package/hooks/hooks.json +30 -1
- package/hooks/hypo-auto-commit.mjs +10 -4
- package/hooks/hypo-auto-minimal-crystallize.mjs +145 -0
- package/hooks/hypo-auto-stage.mjs +4 -3
- package/hooks/hypo-compact-guard.mjs +33 -24
- package/hooks/hypo-cwd-change.mjs +107 -24
- package/hooks/hypo-file-watch.mjs +23 -10
- package/hooks/hypo-first-prompt.mjs +37 -23
- package/hooks/hypo-hot-rebuild.mjs +22 -10
- package/hooks/hypo-lookup.mjs +171 -65
- package/hooks/hypo-personal-check.mjs +207 -112
- package/hooks/hypo-pre-commit.mjs +46 -0
- package/hooks/hypo-session-end.mjs +58 -0
- package/hooks/hypo-session-record.mjs +11 -5
- package/hooks/hypo-session-start.mjs +298 -52
- package/hooks/hypo-shared.mjs +793 -37
- package/hooks/hypo-web-fetch-ingest.mjs +121 -0
- package/hooks/version-check-fetch.mjs +74 -0
- package/hooks/version-check.mjs +184 -0
- package/package.json +17 -3
- package/scripts/crystallize.mjs +623 -18
- package/scripts/doctor.mjs +730 -47
- package/scripts/feedback-sync.mjs +974 -0
- package/scripts/feedback.mjs +253 -44
- package/scripts/graph.mjs +35 -22
- package/scripts/ingest.mjs +89 -16
- package/scripts/init.mjs +398 -113
- package/scripts/lib/design-history-stale.mjs +83 -0
- package/scripts/lib/extensions.mjs +749 -0
- package/scripts/lib/frontmatter.mjs +5 -1
- package/scripts/lib/hypo-ignore.mjs +12 -10
- package/scripts/lib/pkg-json.mjs +23 -5
- package/scripts/lib/project-create.mjs +225 -0
- package/scripts/lib/schema-vocab.mjs +96 -0
- package/scripts/lint.mjs +238 -31
- package/scripts/query.mjs +26 -10
- package/scripts/resume.mjs +11 -5
- package/scripts/session-audit.mjs +37 -27
- package/scripts/smoke-pack.mjs +224 -0
- package/scripts/stats.mjs +24 -10
- package/scripts/uninstall.mjs +363 -49
- package/scripts/upgrade.mjs +706 -202
- package/scripts/verify.mjs +24 -14
- package/scripts/weekly-report.mjs +59 -25
- package/skills/crystallize/SKILL.md +20 -7
- package/skills/ingest/SKILL.md +25 -5
- package/templates/.hypoignore +16 -2
- package/templates/Home.md +2 -0
- package/templates/SCHEMA.md +61 -6
- package/templates/extensions/agents/.gitkeep +0 -0
- package/templates/extensions/commands/.gitkeep +0 -0
- package/templates/extensions/hooks/.gitkeep +0 -0
- package/templates/extensions/skills/.gitkeep +0 -0
- package/templates/gitignore +5 -0
- package/templates/hot.md +2 -0
- package/templates/hypo-config.md +1 -1
- package/templates/hypo-guide.md +42 -2
- package/templates/hypo-help.md +1 -1
- package/templates/pages/observability/_index.md +77 -0
- package/templates/projects/_template/index.md +2 -2
- package/templates/projects/_template/prd.md +1 -1
package/README.ko.md
CHANGED
|
@@ -180,7 +180,7 @@ v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실
|
|
|
180
180
|
|---|---|---|
|
|
181
181
|
| `/hypo:ingest` | 원본을 `sources/`에 보관하고 Claude가 `pages/`에 구조화된 페이지를 합성. 셸 헬퍼(`scripts/ingest.mjs`)는 read-only — 아직 ingest되지 않은 소스를 *목록만* 출력 | 보관할 가치가 있는 글을 읽었을 때 |
|
|
182
182
|
| `/hypo:query` | BM25 검색 + LLM 합성 + `[[wikilink]]` 인용 | 자기 노트에 근거한 답변이 필요할 때 |
|
|
183
|
-
| `/hypo:crystallize` |
|
|
183
|
+
| `/hypo:crystallize` | session-close 체크리스트(steps 1~6) + 요청 시 초안 합성(steps 7~11) | 비자명한 세션 종료 시 |
|
|
184
184
|
| `/hypo:resume` | 활성 프로젝트의 가장 최근 세션 상태 로드 | 일시 중단된 프로젝트로 돌아올 때 |
|
|
185
185
|
| `/hypo:feedback` | AI 행동 교정 기록, 영구 규칙으로 승격 가능 | Claude가 잘못 하거나 정확히 잘 했을 때 |
|
|
186
186
|
| `/hypo:verify` | `verify_by` frontmatter 페이지 감사 | 시간 제약 지식이 노후화됐을 가능성이 있을 때 |
|
|
@@ -196,7 +196,7 @@ v1.0에서는 `personal / shared / public` 3-mode를 만들었습니다. 현실
|
|
|
196
196
|
| `hypo-lookup.mjs` | `UserPromptSubmit` | BM25 top-3 HIT 주입 / MISS → 가까운 슬러그 신호 |
|
|
197
197
|
| `hypo-compact-guard.mjs` | `UserPromptSubmit` | `/compact` 감지 → session-close 체크리스트 강제 |
|
|
198
198
|
| `hypo-cwd-change.mjs` | `CwdChanged` | cwd에 매칭되는 프로젝트 `hot.md` 주입 |
|
|
199
|
-
| `hypo-file-watch.mjs` | `FileChanged` | 위키 파일 변경 알림 |
|
|
199
|
+
| `hypo-file-watch.mjs` | `FileChanged` | 위키 파일 변경 알림 (`.hypoignore` 준수 — 매칭 경로는 LLM 컨텍스트로 재주입되지 않음) |
|
|
200
200
|
| `hypo-auto-stage.mjs` | `PostToolUse(Write/Edit)` | 위키 파일 자동 stage |
|
|
201
201
|
| `hypo-auto-commit.mjs` | `Stop` | 자동 commit + pull + push |
|
|
202
202
|
| `hypo-hot-rebuild.mjs` | `Stop` | `hot.md` 재생성 |
|
|
@@ -298,6 +298,8 @@ Claude가 잘못하거나 정확히 맞았을 때마다 `/hypo:feedback`을 실
|
|
|
298
298
|
|
|
299
299
|
`.hypoignore`는 훅이 무시할 경로를 정의합니다 (기본: `*.pdf`, `*.zip`, `*.pem`, `*.env` 등). 직접 편집하면 됩니다 — privacy mode 플래그는 없습니다. 단일 파일, 단일 진실 소스.
|
|
300
300
|
|
|
301
|
+
> **Provider 전송 디스클레이머.** Hypomnema 훅은 위키 콘텐츠를 Claude Code의 `additionalContext`로 emit하며, 이는 프롬프트의 일부로 Claude 모델 provider에 전송됩니다. `.hypoignore`는 모든 콘텐츠 주입 훅(`hypo-file-watch`, `hypo-session-start`, `hypo-cwd-change`, `hypo-lookup`) 및 `ingest`에서 강제되지만, `.hypoignore`에 매칭되지 *않는* 파일은 전송 대상입니다. (`hypo-auto-stage`/`hypo-auto-commit`은 git staging 훅이며 주입 지점이 아니지만, staging 판단에도 `.hypoignore`를 준수합니다.) 위키에 비밀을 두지 말고, `HYPO_DIR` 아래 민감 콘텐츠를 저장하기 전 `.hypoignore` 패턴을 검토하세요.
|
|
302
|
+
|
|
301
303
|
> **git sync 범위.** Hypomnema는 `~/hypomnema/` 위키 자체만 git sync합니다. Claude Code 설정(`~/.claude/`)은 의도적으로 Hypomnema가 **관리하지 않습니다** — agent·skill·settings의 기기 간 동기화는 [chezmoi](https://www.chezmoi.io/) 같은 별도 dotfiles 매니저 사용을 권장합니다.
|
|
302
304
|
|
|
303
305
|
### `/hypo:*` 커맨드는 어디서 오는가?
|
package/README.md
CHANGED
|
@@ -180,7 +180,7 @@ Eight commands cover the full capture → retrieval → consolidation cycle.
|
|
|
180
180
|
|---|---|---|
|
|
181
181
|
| `/hypo:ingest` | Saves the raw source under `sources/`; Claude synthesizes a structured page under `pages/`. The shell helper (`scripts/ingest.mjs`) is read-only — it only *lists* pending sources so you know what still needs ingesting | Anytime you read something worth keeping |
|
|
182
182
|
| `/hypo:query` | BM25 retrieval + LLM synthesis with `[[wikilink]]` citations | When you need an answer grounded in your own notes |
|
|
183
|
-
| `/hypo:crystallize` |
|
|
183
|
+
| `/hypo:crystallize` | Runs the session-close checklist (steps 1~6) and, on request, synthesizes drafts (steps 7~11) | End of a non-trivial session |
|
|
184
184
|
| `/hypo:resume` | Loads the most recent session state for an active project | Coming back to a paused project |
|
|
185
185
|
| `/hypo:feedback` | Records an AI behavior correction; eligible for promotion to permanent rules | Right when Claude does something wrong (or exactly right) |
|
|
186
186
|
| `/hypo:verify` | Audits pages with `verify_by` frontmatter | When time-bound knowledge might have aged out |
|
|
@@ -196,7 +196,7 @@ Eight commands cover the full capture → retrieval → consolidation cycle.
|
|
|
196
196
|
| `hypo-lookup.mjs` | `UserPromptSubmit` | BM25 top-3 HIT inject / MISS → closest-slug signal |
|
|
197
197
|
| `hypo-compact-guard.mjs` | `UserPromptSubmit` | Detect `/compact` → enforce session-close checklist |
|
|
198
198
|
| `hypo-cwd-change.mjs` | `CwdChanged` | Inject the matching project's `hot.md` |
|
|
199
|
-
| `hypo-file-watch.mjs` | `FileChanged` | Notify on wiki-file changes |
|
|
199
|
+
| `hypo-file-watch.mjs` | `FileChanged` | Notify on wiki-file changes (honors `.hypoignore` — matched paths are never re-emitted into LLM context) |
|
|
200
200
|
| `hypo-auto-stage.mjs` | `PostToolUse(Write/Edit)` | Auto-stage wiki-file edits |
|
|
201
201
|
| `hypo-auto-commit.mjs` | `Stop` | Auto commit + pull + push |
|
|
202
202
|
| `hypo-hot-rebuild.mjs` | `Stop` | Rebuild `hot.md` |
|
|
@@ -298,6 +298,8 @@ Place a `hypo-config.md` at the wiki root to make it portable across machines wi
|
|
|
298
298
|
|
|
299
299
|
`.hypoignore` controls which paths the hooks ignore (default: `*.pdf`, `*.zip`, `*.pem`, `*.env`, …). Edit it directly — there is no privacy mode flag; one file, one source of truth.
|
|
300
300
|
|
|
301
|
+
> **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`.
|
|
302
|
+
|
|
301
303
|
> **Scope of git sync.** Hypomnema git-syncs only the `~/hypomnema/` wiki itself. Your Claude Code configuration (`~/.claude/`) is intentionally **not** managed by Hypomnema — for cross-machine sync of agents, skills, and settings, the recommended pattern is a separate dotfiles manager such as [chezmoi](https://www.chezmoi.io/).
|
|
302
304
|
|
|
303
305
|
### Where do `/hypo:*` commands live?
|
package/commands/audit.md
CHANGED
|
@@ -28,9 +28,9 @@ If the user specified a Hypomnema directory, pass it as `--hypo-dir="<path>"`. O
|
|
|
28
28
|
```bash
|
|
29
29
|
node <package-root>/scripts/session-audit.mjs [--hypo-dir="<path>"] [--limit=20]
|
|
30
30
|
```
|
|
31
|
-
- **Weekly report (when the user asks for "weekly", "score", or names a week)** — write the report to `
|
|
31
|
+
- **Weekly report (when the user asks for "weekly", "score", or names a week)** — write the report to `journal/weekly/<YYYY-Www>.md` (spec §6.4 SoT):
|
|
32
32
|
```bash
|
|
33
|
-
node <package-root>/scripts/weekly-report.mjs [--hypo-dir="<path>"] [--week=YYYY-
|
|
33
|
+
node <package-root>/scripts/weekly-report.mjs [--hypo-dir="<path>"] [--week=YYYY-Www] --write
|
|
34
34
|
```
|
|
35
35
|
- **JSON for tooling** — append `--json` to either script.
|
|
36
36
|
|
package/commands/crystallize.md
CHANGED
|
@@ -4,47 +4,125 @@ description: Crystallize draft notes into stable knowledge — also the session-
|
|
|
4
4
|
|
|
5
5
|
You are running `/hypo:crystallize`. This command serves two purposes:
|
|
6
6
|
|
|
7
|
-
1. **Session close** — if invoked at the end of a session, run the session-close
|
|
7
|
+
1. **Session close** — if invoked at the end of a session, run the session-close mechanical-apply path
|
|
8
8
|
2. **Knowledge synthesis** — consolidate draft or scattered wiki pages into stable, well-linked pages
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
## Step 1 — Detect context
|
|
13
13
|
|
|
14
|
-
If the user invoked `/hypo:crystallize` to close a session (phrases like "세션 종료", "오늘 작업 마무리", "session close", or "wrap up"), run Steps 2–
|
|
14
|
+
If the user invoked `/hypo:crystallize` to close a session (phrases like "세션 종료", "오늘 작업 마무리", "session close", or "wrap up"), run Steps 2–4 (session-close mechanical apply + recovery) **before** the synthesis scan. Otherwise skip to Step 5.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
## Step 2 —
|
|
18
|
+
## Step 2 — Compose the session-close payload
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
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.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
Payload shape (5 required + 1 conditional, per Spec §5.2.7 / §8.3 + ADR 0029):
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"project": "<project-name>",
|
|
27
|
+
"date": "YYYY-MM-DD",
|
|
28
|
+
"sessionState": { "content": "<full body of projects/<name>/session-state.md>" },
|
|
29
|
+
"projectHot": { "content": "<full body of projects/<name>/hot.md>" },
|
|
30
|
+
"rootHot": { "content": "<full body of <hypo-root>/hot.md>" },
|
|
31
|
+
"sessionLog": { "entry": "<entry to append to projects/<name>/session-log/YYYY-MM.md>" },
|
|
32
|
+
"log": { "entry": "<entry to append to <hypo-root>/log.md>" },
|
|
33
|
+
"openQuestions": { "content": "<full body of pages/open-questions.md>" }
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> **Important:** the JSON above is a literal template — do not add `//` or `#` comments when materializing it. `readPayload()` runs `JSON.parse`, which rejects comments and would fail the apply before any write.
|
|
38
|
+
|
|
39
|
+
Field rules:
|
|
40
|
+
|
|
41
|
+
- `project` — optional. Falls back to the active project from root `hot.md` pointer table.
|
|
42
|
+
- `date` — optional. Defaults to today (local). Must be `YYYY-MM-DD` if supplied.
|
|
43
|
+
- `openQuestions` — optional. Include only when `pages/open-questions.md` exists and changed this session.
|
|
44
|
+
- All other top-level fields are required.
|
|
45
|
+
|
|
46
|
+
Notes:
|
|
47
|
+
|
|
48
|
+
- `sessionState` / `projectHot` / `rootHot` / `openQuestions` are **overwrite** (full-file content). `sessionLog` / `log` are **append** (entry-level idempotency — exact-entry dedup, safe to re-run).
|
|
49
|
+
- Frontmatter `updated:` is NOT auto-fixed. If your payload's `updated:` is stale, the post-apply verification gate will fail with `stage='post-apply-verification'` and you must fix the payload and retry.
|
|
50
|
+
- Write the payload to a temp path, e.g. `/tmp/hypo-session-close-<YYYY-MM-DD>.json`.
|
|
51
|
+
|
|
52
|
+
Content guidance for each slot:
|
|
53
|
+
|
|
54
|
+
1. **sessionState** — next tasks list for the upcoming session (what to tackle first next time).
|
|
55
|
+
2. **projectHot** — session snapshot under 500 words: what changed and decisions made. Do **not** put next-step tasks here; those belong in `sessionState`.
|
|
56
|
+
3. **rootHot** — active-projects pointer table with this project's `Last Session` date set to today.
|
|
57
|
+
4. **sessionLog** — one session entry to append to `projects/<name>/session-log/YYYY-MM.md`.
|
|
58
|
+
5. **log** — one `session` entry to append to `<hypo-root>/log.md`.
|
|
59
|
+
6. **openQuestions** (conditional) — only if `pages/open-questions.md` exists and questions were raised or resolved this session.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Step 3 — Apply the payload
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node <package-root>/scripts/crystallize.mjs \
|
|
67
|
+
--apply-session-close \
|
|
68
|
+
--payload=/tmp/hypo-session-close-<YYYY-MM-DD>.json \
|
|
69
|
+
--session-id=<current-session-id> \
|
|
70
|
+
--hypo-dir="<path>" \
|
|
71
|
+
--json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**`--session-id` (fix #27 PR-C):** pass the current session's id whenever you
|
|
75
|
+
know it — most importantly when this close was triggered by a `[WIKI_AUTOCLOSE]`
|
|
76
|
+
Stop-hook block (the block reason prints the exact `--session-id` to use). On a
|
|
77
|
+
verified close (`ok: true` + clean git tree), it writes the per-session marker
|
|
78
|
+
`HYPO_DIR/.cache/session-closed-<id>.marker`. That marker is what tells the
|
|
79
|
+
Stop-chain Layer 3 hook (`hypo-auto-minimal-crystallize`) the session is closed,
|
|
80
|
+
so it stops re-prompting. Omit it only when running crystallize purely for
|
|
81
|
+
synthesis (no session-close intent) — the marker is then simply not written.
|
|
82
|
+
|
|
83
|
+
**Behavior (fix #39 option D + fix #40 lint gates):**
|
|
84
|
+
|
|
85
|
+
| Invocation | Behavior |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `--apply-session-close` (no `--payload`) | **Probe mode** — exits 0 with "오늘 이미 close 완료로 보임" if all 5 files are fresh today; exits 1 with "payload is required" otherwise. Cheap "already complete?" check. |
|
|
88
|
+
| `--apply-session-close --payload=<path>` | **Always-apply** — payload presence = explicit close intent. Per-field idempotent writes (no-op when bytes match), then strict verification + lint gate. Safe to re-run. |
|
|
89
|
+
| `--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. |
|
|
90
|
+
| `--apply-session-close --force` | Skips the probe early-exit. `--payload` still required for any actual apply work. |
|
|
91
|
+
|
|
92
|
+
**Two lint gates run automatically (fix #40):**
|
|
93
|
+
|
|
94
|
+
1. **Preflight** — `lint.mjs --json` runs **before** any payload bytes are written. Errors in files this payload will overwrite (sessionState / projectHot / rootHot / openQuestions) are filtered (they're about to be replaced anyway). Errors in any other file → exit 1 with `stage='preflight-lint'`, no apply occurs.
|
|
95
|
+
2. **Post-apply** — lint re-runs after the writes. Catches payloads that introduce a broken wikilink or malformed body. Surfaces as `stage='post-apply-lint'` (or `'post-apply-verification+lint'` if the freshness gate also fails).
|
|
28
96
|
|
|
29
97
|
---
|
|
30
98
|
|
|
31
|
-
## Step
|
|
99
|
+
## Step 4 — Stage-based recovery
|
|
100
|
+
|
|
101
|
+
The result JSON includes a `stage` field when `ok: false`. Branch on it:
|
|
32
102
|
|
|
33
|
-
|
|
103
|
+
| `stage` | What broke | How to recover |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `preflight-lint` | A non-payload file in the wiki has a blocking lint error. | Fix the lint error in that file (separate from session-close), then re-run. No payload bytes were written. |
|
|
106
|
+
| `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. |
|
|
107
|
+
| `post-apply-lint` | The payload itself introduced a lint blocker (broken wikilink, bad frontmatter). | Fix the offending content in the payload, then re-run. |
|
|
108
|
+
| `post-apply-verification+lint` | Both above. | Fix both; re-run. |
|
|
34
109
|
|
|
35
|
-
|
|
36
|
-
|
|
110
|
+
Once `ok: true`, report:
|
|
111
|
+
|
|
112
|
+
- ✓ session-state.md applied
|
|
113
|
+
- ✓ hot.md (project + root) applied
|
|
37
114
|
- ✓ session-log entry appended
|
|
38
|
-
- ✓ open-questions
|
|
39
|
-
- ✓ log.md
|
|
115
|
+
- ✓ open-questions applied (or skipped if unchanged)
|
|
116
|
+
- ✓ log.md entry appended
|
|
117
|
+
- ✓ post-apply lint clean
|
|
40
118
|
|
|
41
119
|
Then ask: "Session closed. Would you like to also run knowledge synthesis now, or stop here?"
|
|
42
120
|
|
|
43
|
-
If the user says stop, end here. Otherwise continue to Step
|
|
121
|
+
If the user says stop, end here. Otherwise continue to Step 5.
|
|
44
122
|
|
|
45
123
|
---
|
|
46
124
|
|
|
47
|
-
## Step
|
|
125
|
+
## Step 5 — Surface synthesis candidates
|
|
48
126
|
|
|
49
127
|
Locate the Hypomnema package root (the directory containing this file's parent `commands/`).
|
|
50
128
|
|
|
@@ -56,7 +134,7 @@ Show the output to the user. If no candidates are found, tell them Hypomnema loo
|
|
|
56
134
|
|
|
57
135
|
---
|
|
58
136
|
|
|
59
|
-
## Step
|
|
137
|
+
## Step 6 — Choose what to crystallize
|
|
60
138
|
|
|
61
139
|
If candidates exist, ask:
|
|
62
140
|
|
|
@@ -67,7 +145,7 @@ If candidates exist, ask:
|
|
|
67
145
|
|
|
68
146
|
---
|
|
69
147
|
|
|
70
|
-
## Step
|
|
148
|
+
## Step 6a — Tag cluster synthesis
|
|
71
149
|
|
|
72
150
|
For a tag cluster:
|
|
73
151
|
|
|
@@ -89,7 +167,7 @@ For a tag cluster:
|
|
|
89
167
|
|
|
90
168
|
---
|
|
91
169
|
|
|
92
|
-
## Step
|
|
170
|
+
## Step 6b — Draft upgrade
|
|
93
171
|
|
|
94
172
|
For a draft page:
|
|
95
173
|
|
|
@@ -100,7 +178,7 @@ For a draft page:
|
|
|
100
178
|
|
|
101
179
|
---
|
|
102
180
|
|
|
103
|
-
## Step
|
|
181
|
+
## Step 6c — Cross-link unlinked pages
|
|
104
182
|
|
|
105
183
|
For unlinked pages:
|
|
106
184
|
|
|
@@ -111,6 +189,18 @@ For unlinked pages:
|
|
|
111
189
|
|
|
112
190
|
---
|
|
113
191
|
|
|
114
|
-
## Step
|
|
192
|
+
## Step 7 — Report
|
|
115
193
|
|
|
116
194
|
Show what was created or modified, and offer to run `/hypo:lint` to verify all new links resolve.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Appendix — Legacy `--check-session-close`
|
|
199
|
+
|
|
200
|
+
`--check-session-close` (read-only strict gate, same check PreCompact runs) is still supported as a probe-only verification. Use it when you only want to verify that today's session-close is complete without applying anything:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
node <package-root>/scripts/crystallize.mjs --check-session-close [--hypo-dir="<path>"]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
It reports any file as `missing` or `stale`. For an actual close, prefer `--apply-session-close --payload=<path>` (Step 3) — it bundles freshness + lint into one gate and is the documented dogfood path. (`parseArgs` only accepts the `--payload=<path>` spelling; a space-separated `--payload <path>` is silently ignored and triggers "payload is required".)
|
package/commands/feedback.md
CHANGED
|
@@ -2,23 +2,34 @@
|
|
|
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/` for
|
|
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).
|
|
6
6
|
|
|
7
7
|
## What this does
|
|
8
8
|
|
|
9
|
-
- Creates or updates `pages/feedback/<topic>.md` with a dated entry
|
|
9
|
+
- Creates or updates `pages/feedback/<topic>.md` with a dated entry and full classification frontmatter
|
|
10
10
|
- Appends a reference to `log.md`
|
|
11
|
-
-
|
|
11
|
+
- **Automatically refreshes the projection** into `MEMORY.md` and the user's CLAUDE.md `<learned_behaviors>` via `feedback-sync --write`
|
|
12
|
+
|
|
13
|
+
> ⚠️ Do **not** hand-edit MEMORY.md or CLAUDE.md `<learned_behaviors>` for feedback. Those are one-way projections derived from the wiki page. Edit the wiki page; the projection follows.
|
|
12
14
|
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
## Step 1 — Gather feedback details
|
|
16
18
|
|
|
17
|
-
If the user did not provide them, ask:
|
|
19
|
+
If the user did not provide them, ask. The classification fields are required so the page can project correctly:
|
|
20
|
+
|
|
21
|
+
1. **Topic** (slug): "What topic does this feedback apply to? (e.g. `response-length`, `commit-style`)"
|
|
22
|
+
2. **Rule** (entry): "State the rule or correction in one or two sentences."
|
|
23
|
+
3. **Reason**: "What incident or reasoning prompted this?"
|
|
24
|
+
4. **Scope**: "Does this apply globally (all projects) or to this project only?" → `global` | `project:<project-id>` (project-id must exact-match the resolved id; see Step 3 note)
|
|
25
|
+
5. **Tier**: "Is this a hard rule (L1) or a softer preference (L2)?" → `L1` | `L2`
|
|
26
|
+
6. **Targets**: "Where should this project?" → `project-memory` (MEMORY.md) and/or `claude-learned` (global CLAUDE.md). Default `project-memory`.
|
|
27
|
+
7. **Priority** (1–5, higher sorts first; default 3).
|
|
28
|
+
8. **Sensitivity**: `public` (default) or `sanitized` (redacted secrets/paths). `private` is not allowed — the wiki is git-pushed.
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
If **claude-learned** is among the targets, the page must be `scope: global` + `tier: L1`, and you must also collect:
|
|
31
|
+
- **Global summary**: a one-line summary for the CLAUDE.md learned-behaviors entry.
|
|
32
|
+
- Confirm **promote to global** (the page is only projected to CLAUDE.md when promoted).
|
|
22
33
|
|
|
23
34
|
---
|
|
24
35
|
|
|
@@ -30,38 +41,41 @@ To check for an existing topic, locate the Hypomnema package root and run:
|
|
|
30
41
|
node <package-root>/scripts/feedback.mjs --list [--hypo-dir="<path>"]
|
|
31
42
|
```
|
|
32
43
|
|
|
33
|
-
If a matching topic exists,
|
|
44
|
+
If a matching topic exists, appending adds a dated entry and bumps `updated:` (classification frontmatter is preserved).
|
|
34
45
|
|
|
35
46
|
---
|
|
36
47
|
|
|
37
|
-
## Step 3 — Write the feedback
|
|
38
|
-
|
|
39
|
-
Compose the entry text. Format:
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
**Rule**: <one-line rule>
|
|
43
|
-
|
|
44
|
-
**Why**: <reason or incident>
|
|
45
|
-
|
|
46
|
-
**How to apply**: <when this kicks in>
|
|
47
|
-
```
|
|
48
|
+
## Step 3 — Write the feedback page
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
Run with `--dry-run` first to preview the generated page, then without it to write. Pass every collected field:
|
|
50
51
|
|
|
51
52
|
```bash
|
|
52
53
|
node <package-root>/scripts/feedback.mjs \
|
|
53
54
|
--topic="<slug>" \
|
|
54
|
-
--entry="<
|
|
55
|
+
--entry="<one-line rule>" \
|
|
56
|
+
--scope="global|project:<project-id>" \
|
|
57
|
+
--tier="L1|L2" \
|
|
58
|
+
--targets="project-memory[,claude-learned]" \
|
|
59
|
+
--priority=<1-5> \
|
|
60
|
+
--sensitivity="public|sanitized" \
|
|
61
|
+
--memory-summary="<one-line MEMORY.md summary>" \
|
|
62
|
+
--reason="<why this rule exists>" \
|
|
63
|
+
[--global-summary="<one-line CLAUDE.md summary>" --promote-to-global] \
|
|
64
|
+
[--source="session:<date>"] \
|
|
55
65
|
[--hypo-dir="<path>"] \
|
|
56
66
|
[--dry-run]
|
|
57
67
|
```
|
|
58
68
|
|
|
59
|
-
|
|
69
|
+
When `--targets` includes `claude-learned`, `--global-summary` and `--promote-to-global` are required (and `--scope=global --tier=L1`).
|
|
70
|
+
|
|
71
|
+
> **`scope: project:<project-id>` 주의 (v1.2.0).** `<project-id>`는 `feedback-sync`가 resolve한 project-id와 정확히 일치해야 한다 (default: cwd의 `/`,`.` → `-` 치환; `--project-id=<id>` 로 override). 일치하지 않으면 그 페이지는 해당 project의 MEMORY로 projection되지 **않는다** (silent skip — lint error 아님). 다만 현재 lint scope regex(`^project:[a-z0-9][a-z0-9-]*$`)는 cwd-derived id 형식(`-Users-...`)을 거부하므로, **`project:*` scope를 사용하려면 slug-safe id로 `--project-id=<slug>`를 override해서 wiki 디렉터리도 그 id에 맞추는 운영 패턴이 필요하다**. resolved-id ↔ slug 정합화는 v1.3.0 트랙에서 다룸.
|
|
72
|
+
|
|
73
|
+
On a real (non-dry-run) write, the script automatically runs `feedback-sync --write` to refresh MEMORY.md / CLAUDE.md. If that post-step reports drift it prints a one-line warning — the page is still saved; reconcile with `hypomnema feedback-sync --check`.
|
|
60
74
|
|
|
61
75
|
---
|
|
62
76
|
|
|
63
|
-
## Step 4 — Confirm
|
|
77
|
+
## Step 4 — Confirm
|
|
64
78
|
|
|
65
|
-
After writing:
|
|
66
|
-
-
|
|
67
|
-
- If
|
|
79
|
+
After writing, tell the user:
|
|
80
|
+
- "Saved to `pages/feedback/<topic>.md` and refreshed the MEMORY/CLAUDE projection."
|
|
81
|
+
- If the projection post-step warned (over-cap, conflict, unresolved project-id), surface that and suggest `hypomnema feedback-sync --check`.
|
package/commands/ingest.md
CHANGED
|
@@ -21,13 +21,33 @@ Ask the user:
|
|
|
21
21
|
2. **Slug**: "What slug should this source have? (e.g. `openai-swarm-paper`, `team-retro-2026-04`)"
|
|
22
22
|
- Default: derive from title or filename
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Do **not** fetch the URL or read the file yet — the privacy guard in Step 2 must run first.
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
## Step 2 —
|
|
28
|
+
## Step 2 — Privacy guard (`.hypoignore`)
|
|
29
29
|
|
|
30
|
-
Locate the Hypomnema package root
|
|
30
|
+
Refuse to ingest secrets (`.env`, SSH keys, credentials) before they ever reach `sources/`. Locate the Hypomnema package root and run the guard for **both** the input path and the destination path:
|
|
31
|
+
|
|
32
|
+
1. **If the source is a file path**, check it (use an absolute path):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"] --check="<absolute-input-path>"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. **Always** check the destination `sources/<slug>.<ext>`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"] --check="sources/<slug>.<ext>"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If either command exits non-zero, **stop**: surface the `Refused: ...` message to the user and do not fetch, read, or save the source. The slug check matters because a user could rename a `.env` to an innocuous slug — the destination must still be blocked.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 3 — Check for orphaned sources
|
|
49
|
+
|
|
50
|
+
Run the ingest helper to surface existing orphaned sources:
|
|
31
51
|
|
|
32
52
|
```bash
|
|
33
53
|
node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"]
|
|
@@ -35,9 +55,11 @@ node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"]
|
|
|
35
55
|
|
|
36
56
|
If there are orphaned sources already in `sources/`, ask: "There are N unprocessed sources — do you want to ingest one of those instead?"
|
|
37
57
|
|
|
58
|
+
Once the guard has passed: if a URL is provided, fetch the content; if a file path is provided, read it.
|
|
59
|
+
|
|
38
60
|
---
|
|
39
61
|
|
|
40
|
-
## Step
|
|
62
|
+
## Step 4 — Save raw source
|
|
41
63
|
|
|
42
64
|
Save the raw content to `sources/<slug>.<ext>` (use `.md` for text, `.txt` for plain text, `.pdf` or original extension for documents).
|
|
43
65
|
|
|
@@ -45,13 +67,13 @@ Do **not** modify or summarize in the sources file — save it as-is.
|
|
|
45
67
|
|
|
46
68
|
---
|
|
47
69
|
|
|
48
|
-
## Step
|
|
70
|
+
## Step 5 — Synthesize
|
|
49
71
|
|
|
50
72
|
Read and synthesize the source:
|
|
51
73
|
|
|
52
74
|
1. **Check index.md** — does a page on this topic already exist?
|
|
53
75
|
- If yes: update the existing page (merge new information, mark `updated:` today)
|
|
54
|
-
- If no: create a new page in `pages/` with `type: source-summary` and `
|
|
76
|
+
- If no: create a new page in `pages/` with `type: source-summary` and `sources: [<slug>]`
|
|
55
77
|
|
|
56
78
|
2. **Frontmatter** for new pages:
|
|
57
79
|
```yaml
|
|
@@ -60,7 +82,7 @@ Read and synthesize the source:
|
|
|
60
82
|
type: source-summary
|
|
61
83
|
updated: YYYY-MM-DD
|
|
62
84
|
tags: [<relevant tags>]
|
|
63
|
-
|
|
85
|
+
sources: [<slug>]
|
|
64
86
|
confidence: high | medium | low
|
|
65
87
|
evidence_strength: direct
|
|
66
88
|
---
|
|
@@ -70,14 +92,14 @@ Read and synthesize the source:
|
|
|
70
92
|
|
|
71
93
|
---
|
|
72
94
|
|
|
73
|
-
## Step
|
|
95
|
+
## Step 6 — Update index.md and log.md
|
|
74
96
|
|
|
75
97
|
- Append a line to `index.md`: `- [[pages/<slug>]] — <one-line description>`
|
|
76
98
|
- Append to `log.md`: `- YYYY-MM-DD ingest: [[pages/<slug>]] from sources/<slug>.<ext>`
|
|
77
99
|
|
|
78
100
|
---
|
|
79
101
|
|
|
80
|
-
## Step
|
|
102
|
+
## Step 7 — Report
|
|
81
103
|
|
|
82
104
|
Show:
|
|
83
105
|
- ✓ Saved source: `sources/<slug>.<ext>`
|
package/commands/upgrade.md
CHANGED
|
@@ -28,7 +28,7 @@ node <package-root>/scripts/upgrade.mjs [--hypo-dir="<path>"]
|
|
|
28
28
|
|
|
29
29
|
Show the output verbatim.
|
|
30
30
|
|
|
31
|
-
> **Note**:
|
|
31
|
+
> **Note**: A major SCHEMA bump is only **detected** in this step. The informational `MIGRATION-vX.Y.md` file is written later by `--apply` (Step 4) and only on a major bump. `SCHEMA.md` is never auto-overwritten.
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ Show the output verbatim.
|
|
|
38
38
|
- `⚠` — minor update available (stale hook or missing settings entry)
|
|
39
39
|
- `✗` — major version bump or missing hook files (action required)
|
|
40
40
|
|
|
41
|
-
For a **major SCHEMA bump**:
|
|
41
|
+
For a **major SCHEMA bump**: warn the user that `--apply` will additionally write a `MIGRATION-vX.Y.md` informational file in their Hypomnema root and that they must manually merge the SCHEMA diff after applying.
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -328,7 +328,7 @@ scripts/session-audit.mjs ← per-session metrics + classificat
|
|
|
328
328
|
scripts/weekly-report.mjs ← aggregated weekly autonomy score
|
|
329
329
|
│
|
|
330
330
|
▼
|
|
331
|
-
|
|
331
|
+
journal/weekly/<YYYY-Www>.md ← committed report (heuristic v0, spec §6.4 SoT)
|
|
332
332
|
```
|
|
333
333
|
|
|
334
334
|
### Transcript dual-source (ADR 0019)
|
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -120,7 +120,7 @@ If you need to share new logic, prefer extending an existing helper over adding
|
|
|
120
120
|
|
|
121
121
|
```bash
|
|
122
122
|
npm test # tests/runner.mjs — unit + smoke + contract tests
|
|
123
|
-
npm run lint # scripts/lint.mjs — frontmatter + wikilink validation
|
|
123
|
+
npm run lint # scripts/lint.mjs — frontmatter + wikilink validation + W8 (design-history stale vs session-log)
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
The test runner uses only Node.js built-ins. Tests create scoped temp directories and clean up after themselves; you can run the suite without any environment setup.
|
package/hooks/hooks.json
CHANGED
|
@@ -11,6 +11,17 @@
|
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
|
+
"SessionEnd": [
|
|
15
|
+
{
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/hypo-session-end.mjs",
|
|
20
|
+
"timeout": 10
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
14
25
|
"UserPromptSubmit": [
|
|
15
26
|
{
|
|
16
27
|
"hooks": [
|
|
@@ -60,6 +71,15 @@
|
|
|
60
71
|
"timeout": 10
|
|
61
72
|
}
|
|
62
73
|
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"hooks": [
|
|
77
|
+
{
|
|
78
|
+
"type": "command",
|
|
79
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/hypo-web-fetch-ingest.mjs",
|
|
80
|
+
"timeout": 10
|
|
81
|
+
}
|
|
82
|
+
]
|
|
63
83
|
}
|
|
64
84
|
],
|
|
65
85
|
"Stop": [
|
|
@@ -89,6 +109,15 @@
|
|
|
89
109
|
"timeout": 60
|
|
90
110
|
}
|
|
91
111
|
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"hooks": [
|
|
115
|
+
{
|
|
116
|
+
"type": "command",
|
|
117
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/hypo-auto-minimal-crystallize.mjs",
|
|
118
|
+
"timeout": 10
|
|
119
|
+
}
|
|
120
|
+
]
|
|
92
121
|
}
|
|
93
122
|
],
|
|
94
123
|
"CwdChanged": [
|
|
@@ -114,5 +143,5 @@
|
|
|
114
143
|
}
|
|
115
144
|
]
|
|
116
145
|
},
|
|
117
|
-
"shared": ["hypo-shared.mjs"]
|
|
146
|
+
"shared": ["hypo-shared.mjs", "version-check.mjs", "version-check-fetch.mjs"]
|
|
118
147
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { spawnSync } from 'child_process';
|
|
9
|
-
import { HYPO_DIR, loadHypoIgnore, isIgnored } from './hypo-shared.mjs';
|
|
9
|
+
import { HYPO_DIR, loadHypoIgnore, isIgnored, appendSyncFailure } from './hypo-shared.mjs';
|
|
10
10
|
import { join } from 'path';
|
|
11
11
|
|
|
12
12
|
function git(...args) {
|
|
@@ -27,7 +27,8 @@ for (const line of (porcelain.stdout || '').split('\n')) {
|
|
|
27
27
|
if (!line) continue;
|
|
28
28
|
const file = line.slice(3).replace(/^"|"$/g, '').split(' -> ').pop().trim();
|
|
29
29
|
if (!file) continue;
|
|
30
|
-
if (ignorePatterns.length > 0 && isIgnored(join(HYPO_DIR, file), HYPO_DIR, ignorePatterns))
|
|
30
|
+
if (ignorePatterns.length > 0 && isIgnored(join(HYPO_DIR, file), HYPO_DIR, ignorePatterns))
|
|
31
|
+
continue;
|
|
31
32
|
paths.push(file);
|
|
32
33
|
}
|
|
33
34
|
if (paths.length > 0) git('add', '--', ...paths);
|
|
@@ -42,8 +43,13 @@ if (staged) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
if (hasRemote()) {
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
// fix #9: pull/push failures must not stop the session, but they can no
|
|
47
|
+
// longer be swallowed silently — record each to .cache/sync-state.json so
|
|
48
|
+
// session-start (#10) and doctor (#11) can surface them next session.
|
|
49
|
+
const pull = git('pull', '--no-rebase', '-q');
|
|
50
|
+
if (pull.status !== 0) appendSyncFailure(HYPO_DIR, 'pull', pull.stderr || pull.stdout);
|
|
51
|
+
const push = git('push');
|
|
52
|
+
if (push.status !== 0) appendSyncFailure(HYPO_DIR, 'push', push.stderr || push.stdout);
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
|