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,14 +11,14 @@ When invoked to close a session — via an explicit close signal ("세션 종료
11
11
 
12
12
  ## What this does
13
13
 
14
- - **Close mode**: walks the checklist (session-state, project hot.md, root hot.md, session-log, open-questions(변경 시), log.md) plus a lint step, then writes via `crystallize.mjs --apply-session-close --payload=<path>` — which runs the lint gate automatically, **scoped to the files it writes** (debt elsewhere is a non-blocking notice). `--check-session-close` is a read-only dry-run of the **full** PreCompact gate (ADR 0046) — close files + scoped lint + design-history + feedback projection sharing one function (`precompactGateStatus`) with the gate. A green check means no gate blocker needs a human fix, so it is the signal to declare the session closed (pass `--transcript-path` to widen the lint scope to this session's edited files exactly as the interactive hook does). It is not a hard guarantee: the live `/compact` can still differ on a context-≥70% prompt, `HYPO_SKIP_GATE`, or a transcript-scoped lint error the check did not see.
14
+ - **Close mode**: walks the checklist (session-state, project hot.md, root hot.md, session-log, open-questions(변경 시), log.md) plus a lint step, then writes via `/hypo:crystallize` in `--apply-session-close --payload=<path>` mode — which runs the lint gate automatically, **scoped to the files it writes** (debt elsewhere is a non-blocking notice). `--check-session-close` is a read-only dry-run of the **full** PreCompact gate: close files + scoped lint + design-history + feedback projection, sharing one function (`precompactGateStatus`) with the gate. A green check means no gate blocker needs a human fix, so it is the signal to declare the session closed (pass `--transcript-path` to widen the lint scope to this session's edited files exactly as the interactive hook does). It is not a hard guarantee: the live `/compact` can still differ on a context-≥70% prompt, `HYPO_SKIP_GATE`, or a transcript-scoped lint error the check did not see.
15
15
  - **Synthesis mode**: finds tag clusters (≥ N pages), orphan pages (no outbound `[[wikilinks]]`), and draft / stub pages, then guides consolidation into `pages/syntheses/<topic>.md` with back-links and `index.md` updates.
16
16
 
17
17
  ---
18
18
 
19
19
  ## Step 1 — Locate package root
20
20
 
21
- Locate the Hypomnema package root (the directory two levels above this file (`skills/<name>/SKILL.md` package root)).
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 wiki directory, pass it as `--wiki-dir="<path>"`. Otherwise omit the flag and the script resolves the wiki root automatically via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
24
24
 
@@ -27,7 +27,7 @@ If the user specified a wiki directory, pass it as `--wiki-dir="<path>"`. Otherw
27
27
  ## Step 2 — Run crystallize scan
28
28
 
29
29
  ```bash
30
- node <package-root>/scripts/crystallize.mjs \
30
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/crystallize.mjs \
31
31
  [--wiki-dir="<path>"] \
32
32
  [--min-group=<n>] \
33
33
  [--json]
@@ -47,7 +47,7 @@ If `/hypo:crystallize` was invoked as a session-close action, run through this c
47
47
 
48
48
  ### Advisory reflections (run before the checklist below — advisory only)
49
49
 
50
- Surface each of these four to the user first. 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.
50
+ Surface each of these four to the user first. 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.
51
51
 
52
52
  - **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를 건너뛸까요?"* 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.
53
53
  - **ADR-candidate check (#41)** — Did this session make an architectural or design decision (a new pattern, a tradeoff, a convention)? If yes, ask whether it warrants an ADR and capture that intent in the session-log entry. If nothing rose to ADR level, you may record the literal marker `ADR 없음 — <one-line reason>` in that same session-log entry — but gate it on #42's bar, not this one: the marker is machine-read and W8 treats a session-log entry carrying `ADR 없음` (and no ADR reference) as a *no-design* session, excluding it from the design-history staleness check. So write `ADR 없음` only when the session had no design change at all. If it had a sub-ADR design shift (background / tradeoff / differentiation), append to design-history (#42a) instead — writing the marker there would suppress the W8 nudge that shift needs. **Never auto-write an ADR file** — the session-log note is the only action here.
@@ -67,11 +67,11 @@ When uncertain, surface the question rather than skip it. None of the four block
67
67
  After completing the checklist, verify it before reporting:
68
68
 
69
69
  ```bash
70
- node <package-root>/scripts/crystallize.mjs --check-session-close [--hypo-dir="<path>"]
70
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/crystallize.mjs --check-session-close [--hypo-dir="<path>"]
71
71
  ```
72
72
 
73
- This runs the **full** PreCompact gate via the shared `precompactGateStatus`
74
- (ADR 0046): close files (`missing` / `stale`) plus lint blockers, stale
73
+ This runs the **full** PreCompact gate via the shared `precompactGateStatus`:
74
+ close files (`missing` / `stale`) plus lint blockers, stale
75
75
  design-history, and feedback projection over-cap/conflict. Fix every `✗` it
76
76
  reports and re-run until it prints **"Compact-ready"** — that is the signal the
77
77
  session is closed. A close-files-only pass is not enough; the real `/compact`
@@ -92,7 +92,9 @@ main conversation id. Passing the wrong id causes `markerWritten: false` with
92
92
  `markerSkipReason: "transcript-unresolved"` or `"no-user-close-signal"`: the
93
93
  5 mandatory files are written (ok: true) but the Stop-chain marker is withheld,
94
94
  so the session is not actually closed and the Stop hook re-prompts. The correct
95
- id comes from the `[WIKI_AUTOCLOSE]` block reason or `$CLAUDE_SESSION_ID`.
95
+ id comes from the `[WIKI_AUTOCLOSE]` block reason or the injected
96
+ `$CLAUDE_CODE_SESSION_ID` (accept the legacy spelling via
97
+ `${CLAUDE_CODE_SESSION_ID:-$CLAUDE_SESSION_ID}`).
96
98
 
97
99
  Once it passes, report each item with ✓:
98
100
  - ✓ session-state.md
@@ -100,11 +102,15 @@ Once it passes, report each item with ✓:
100
102
  - ✓ session-log entry
101
103
  - ✓ open-questions (or skipped if unchanged)
102
104
  - ✓ log.md entry
103
- - **marker written?** (required, if `--apply-session-close --session-id` was used): check `markerWritten` in the JSON output. If `true`, report "session-close marker written." If `false`, do NOT declare the session closed or complete. Instead report: "Files applied and verified (ok: true), but the session-close marker was not written (reason: `<markerSkipReason>`). The Stop-chain is still active. Re-run with the correct main-conversation `--session-id`."
105
+ - **marker written?** (required, if `--apply-session-close --session-id` was used): check `markerWritten` in the JSON output. If `true`, report "session-close marker written." If `false`, do NOT declare the session closed or complete; recover per the `markerSkipReason` branch below.
104
106
 
105
107
  If `markerWritten: true` (or `--session-id` was not passed), ask: "Session closed. Would you like to also run knowledge synthesis now, or stop here?"
106
108
 
107
- If `markerWritten: false`, do NOT say "session closed." Surface the skip reason and instruct a re-run with the correct main-conversation `--session-id` (see the bullet above) before treating the session as closed.
109
+ If `markerWritten: false`, do NOT say "session closed." Branch on `markerSkipReason`:
110
+
111
+ - `no-user-close-signal`: files applied cleanly and the transcript resolved, but it carries no close phrase the gate recognizes (the user's close wording fell outside the close-signal set, e.g. "세션 마무리까지 진행해줘" / "세션 마무리 진행"). Re-running the same id will not help. Confirm intent once with `AskUserQuestion` (header "세션", one option **세션 마무리**); if the user picks it, that answer is a recognized close signal, so re-run the same `--apply-session-close … --session-id` command (idempotent no-op writes; the marker lands). If the user declines, leave the session unmarked. Do NOT change the close-signal matcher.
112
+ - `transcript-unresolved` (wrong / background id): report "Files applied and verified (ok: true), but the marker was not written (reason: `<markerSkipReason>`). The Stop-chain is still active. Re-run with the correct main-conversation `--session-id`."
113
+ - any other reason (`compact-gate-not-ok`, `commit-failed: …`, `marker-did-not-land`): surface it verbatim and resolve the underlying blocker before re-running.
108
114
 
109
115
  ---
110
116
 
@@ -141,4 +147,4 @@ evidence_strength: inferred
141
147
 
142
148
  ---
143
149
 
144
- > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
150
+ > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]` so it stays connected in the graph. The observability audit scores sessions on search / ingest / feedback activity (recorded by `hypo-session-record`), not on these inline citations; run `/hypo:audit` to inspect and see [[pages/observability/_index]].
@@ -14,7 +14,7 @@ You are running `/hypo:graph`. Build a wikilink dependency graph from all pages
14
14
 
15
15
  ## Step 1 — Locate package root
16
16
 
17
- Locate the Hypomnema package root (the directory two levels above this file (`skills/<name>/SKILL.md` package root)).
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 wiki directory, pass it as `--wiki-dir="<path>"`. Otherwise omit the flag and the script resolves the wiki root automatically via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
20
20
 
@@ -37,7 +37,7 @@ Optionally ask:
37
37
  ## Step 3 — Run graph
38
38
 
39
39
  ```bash
40
- node <package-root>/scripts/graph.mjs \
40
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/graph.mjs \
41
41
  [--wiki-dir="<path>"] \
42
42
  [--format=json|mermaid|dot] \
43
43
  [--min-edges=<n>]
@@ -55,4 +55,4 @@ If the graph has 0 edges, note that no `[[wikilinks]]` were found between pages.
55
55
 
56
56
  ---
57
57
 
58
- > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
58
+ > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]` so it stays connected in the graph. The observability audit scores sessions on search / ingest / feedback activity (recorded by `hypo-session-record`), not on these inline citations; run `/hypo:audit` to inspect and see [[pages/observability/_index]].
@@ -14,7 +14,7 @@ You are running `/hypo:ingest`. Add a new source document to `sources/` and crea
14
14
 
15
15
  ## Step 1 — Locate package root
16
16
 
17
- Locate the Hypomnema package root (the directory two levels above this file (`skills/<name>/SKILL.md` package root)).
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 wiki directory, pass it as `--hypo-dir="<path>"`. Otherwise omit the flag and the script resolves the wiki root automatically via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
20
20
 
@@ -23,7 +23,7 @@ If the user specified a wiki directory, pass it as `--hypo-dir="<path>"`. Otherw
23
23
  ## Step 2 — Run ingest status check
24
24
 
25
25
  ```bash
26
- node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"] [--json]
26
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/ingest.mjs [--hypo-dir="<path>"] [--json]
27
27
  ```
28
28
 
29
29
  Options:
@@ -40,13 +40,13 @@ Before touching any source content, refuse to ingest secrets (`.env`, SSH keys,
40
40
  1. **If the user provided a file path**, check it (use an absolute path):
41
41
 
42
42
  ```bash
43
- node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"] --check="<absolute-input-path>"
43
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/ingest.mjs [--hypo-dir="<path>"] --check="<absolute-input-path>"
44
44
  ```
45
45
 
46
46
  2. **Always** check the destination `sources/<slug>.<ext>`:
47
47
 
48
48
  ```bash
49
- node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"] --check="sources/<slug>.<ext>"
49
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/ingest.mjs [--hypo-dir="<path>"] --check="sources/<slug>.<ext>"
50
50
  ```
51
51
 
52
52
  If either command exits non-zero, **stop**: surface the `Refused: ...` message to the user and do not download, 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.
@@ -104,4 +104,4 @@ Append an ingest entry to `<wiki-root>/log.md`:
104
104
 
105
105
  ---
106
106
 
107
- > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
107
+ > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]` so it stays connected in the graph. The observability audit scores sessions on search / ingest / feedback activity (recorded by `hypo-session-record`), not on these inline citations; run `/hypo:audit` to inspect and see [[pages/observability/_index]].
@@ -16,7 +16,7 @@ You are running `/hypo:lint`. Validate all wiki pages for frontmatter correctnes
16
16
 
17
17
  ## Step 1 — Locate package root
18
18
 
19
- Locate the Hypomnema package root (the directory two levels above this file (`skills/<name>/SKILL.md` package root)).
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 wiki directory, pass it as `--wiki-dir="<path>"`. Otherwise omit the flag and the script resolves the wiki root automatically via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
22
22
 
@@ -25,7 +25,7 @@ If the user specified a wiki directory, pass it as `--wiki-dir="<path>"`. Otherw
25
25
  ## Step 2 — Run lint
26
26
 
27
27
  ```bash
28
- node <package-root>/scripts/lint.mjs [--wiki-dir="<path>"] [--json] [--fix]
28
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/lint.mjs [--wiki-dir="<path>"] [--json] [--fix]
29
29
  ```
30
30
 
31
31
  Options:
@@ -56,4 +56,4 @@ For **missing required fields** (`title`, `type`): open the affected files and h
56
56
 
57
57
  ---
58
58
 
59
- > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
59
+ > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]` so it stays connected in the graph. The observability audit scores sessions on search / ingest / feedback activity (recorded by `hypo-session-record`), not on these inline citations; run `/hypo:audit` to inspect and see [[pages/observability/_index]].
@@ -14,7 +14,7 @@ You are running `/hypo:query`. Full-text search across all wiki pages and projec
14
14
 
15
15
  ## Step 1 — Locate package root
16
16
 
17
- Locate the Hypomnema package root (the directory two levels above this file (`skills/<name>/SKILL.md` package root)).
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 wiki directory, pass it as `--wiki-dir="<path>"`. Otherwise omit the flag and the script resolves the wiki root automatically via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
20
20
 
@@ -31,7 +31,7 @@ Use the search terms from the user's message. If no query was provided, ask:
31
31
  ## Step 3 — Run query
32
32
 
33
33
  ```bash
34
- node <package-root>/scripts/query.mjs \
34
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/query.mjs \
35
35
  --q="<search terms>" \
36
36
  [--wiki-dir="<path>"] \
37
37
  [--limit=<n>] \
@@ -59,4 +59,4 @@ If zero results are returned, say so and offer to broaden the search or suggest
59
59
 
60
60
  ---
61
61
 
62
- > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
62
+ > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]` so it stays connected in the graph. The observability audit scores sessions on search / ingest / feedback activity (recorded by `hypo-session-record`), not on these inline citations; run `/hypo:audit` to inspect and see [[pages/observability/_index]].
@@ -14,7 +14,7 @@ You are running `/hypo:verify`. Check all wiki pages for `verify_by` and `verify
14
14
 
15
15
  ## Step 1 — Locate package root
16
16
 
17
- Locate the Hypomnema package root (the directory two levels above this file (`skills/<name>/SKILL.md` package root)).
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 wiki directory, pass it as `--wiki-dir="<path>"`. Otherwise omit the flag and the script resolves the wiki root automatically via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
20
20
 
@@ -23,7 +23,7 @@ If the user specified a wiki directory, pass it as `--wiki-dir="<path>"`. Otherw
23
23
  ## Step 2 — Run verify
24
24
 
25
25
  ```bash
26
- node <package-root>/scripts/verify.mjs \
26
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/verify.mjs \
27
27
  [--wiki-dir="<path>"] \
28
28
  [--file="<path>"] \
29
29
  [--json]
@@ -93,4 +93,4 @@ updated: <today YYYY-MM-DD>
93
93
 
94
94
  ---
95
95
 
96
- > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
96
+ > **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]` so it stays connected in the graph. The observability audit scores sessions on search / ingest / feedback activity (recorded by `hypo-session-record`), not on these inline citations; run `/hypo:audit` to inspect and see [[pages/observability/_index]].
@@ -96,7 +96,7 @@ verify_by: <question to re-check at next review>
96
96
  verify_by_date: YYYY-MM-DD
97
97
  ```
98
98
 
99
- ### 3.1. `feedback` type — projection fields (ADR 0031)
99
+ ### 3.1. `feedback` type — projection fields
100
100
 
101
101
  Feedback pages are the single source of truth for behavior corrections;
102
102
  `hypomnema feedback-sync` projects them one-way into Claude Code's `MEMORY.md`
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: Hypomnema Config
3
3
  type: config
4
- version: "1.4.2"
4
+ version: "1.5.1"
5
5
  created: YYYY-MM-DD
6
6
  ---
7
7
 
@@ -67,7 +67,7 @@ Trigger: explicit close mention, `/compact` request, or context limit approachin
67
67
 
68
68
  **Auto-trigger rule**: when the user sends a natural-language close signal — "세션 마무리하자", "오늘 여기까지", "이만 종료", "wrap up", "signing off", or equivalent — run the session close checklist immediately without waiting for an explicit `/compact` or `/hypo:crystallize`. If the intent is ambiguous, confirm with a single question before proceeding.
69
69
 
70
- **Proactive close offer** (ADR 0022 Layer 1): even when the user gives no close signal, offer to wrap up once a task is truly done — don't make them remember to. Fire `AskUserQuestion` **at the end of the assistant turn in which you report a task complete or verified**, when both hold:
70
+ **Proactive close offer** (Layer 1): even when the user gives no close signal, offer to wrap up once a task is truly done — don't make them remember to. Fire `AskUserQuestion` **at the end of the assistant turn in which you report a task complete or verified**, when both hold:
71
71
 
72
72
  1. The session did substantial work — file mutations or multi-step changes, not Q&A only.
73
73
  2. The current user request did not already include a next task or tell you not to close.
@@ -82,13 +82,13 @@ Ask: *"이 작업이 마무리되었나요? 세션을 정리(crystallize)할까
82
82
  2. Update `projects/<name>/hot.md` (what was done, ≤500 words, overwrite)
83
83
  3. Append to `projects/<name>/session-log/YYYY-MM-DD.md` (daily shard, narrative entry, append-only)
84
84
  4. Update root `hot.md` pointer table + date
85
- 5. Run `scripts/lint.mjs` and fix errors in files **you** touched — debt in other
85
+ 5. Run `/hypo:lint` and fix errors in files **you** touched — debt in other
86
86
  projects / shared pages you did not author is reported as a non-blocking
87
- notice, not a gate. (The documented `crystallize.mjs --apply-session-close`
88
- path runs this lint automatically, scoped to the files it writes.)
89
- 6. Verify with `scripts/crystallize.mjs --check-session-close`: a dry-run of the
87
+ notice, not a gate. (The documented `/hypo:crystallize` session-close path
88
+ runs this lint automatically, scoped to the files it writes.)
89
+ 6. Verify with `/hypo:crystallize` in its `--check-session-close` mode: a dry-run of the
90
90
  **full** PreCompact gate (close files + lint + design-history + feedback
91
- projection, ADR 0046), sharing one function with the gate. Only declare the
91
+ projection), sharing one function with the gate. Only declare the
92
92
  session closed once it prints **"Compact-ready"**. A "close files updated"
93
93
  check alone is not enough — the real `/compact` gate also blocks on a lint
94
94
  error in a close file or a feedback projection over-cap. (Not a hard
@@ -99,13 +99,13 @@ Ask: *"이 작업이 마무리되었나요? 세션을 정리(crystallize)할까
99
99
  diagnostic, JSON `scope: "project"`): green there means only that slug is
100
100
  close-complete, **not** that `/compact` is globally unblocked. Use the plain
101
101
  check for the go/no-go signal.
102
- 7. Record the session-closed marker (ADR 0047). The Stop hook blocks until this
102
+ 7. Record the session-closed marker. The Stop hook blocks until this
103
103
  session's per-session marker exists, and a hand-edit close (writing the files
104
104
  directly + committing) never writes it; the marker is written only by the
105
- crystallize writer, never by the hook (ADR 0022 bypass guard). Normal path:
106
- close via `crystallize.mjs --apply-session-close --session-id=<id> --transcript-path=<path>`,
105
+ crystallize writer, never by the hook (bypass guard). Normal path:
106
+ close via `/hypo:crystallize` (`--apply-session-close --session-id=<id> --transcript-path=<path>`),
107
107
  which writes the marker once the gate is green. Hand-edit recovery: after
108
- committing the files, run `crystallize.mjs --mark-session-closed --session-id=<id> --transcript-path=<path>`.
108
+ committing the files, run `/hypo:crystallize` (`--mark-session-closed --session-id=<id> --transcript-path=<path>`).
109
109
  Both writers gate the marker on the SAME `precompactGateStatus` as `/compact`,
110
110
  so the marker only lands when step 6 would print **"Compact-ready"**.
111
111
 
@@ -153,7 +153,7 @@ projects/<name>/
153
153
 
154
154
  Add to root `hot.md` active projects table.
155
155
 
156
- #### Auto-project offer (ADR 0023)
156
+ #### Auto-project offer
157
157
 
158
158
  When SessionStart / CwdChanged injects a line like
159
159
  `[WIKI: cwd '<name>'에 매칭되는 프로젝트가 없습니다. 자동 생성할까요? (Y/n)]`,
@@ -166,8 +166,14 @@ ignore it:**
166
166
  2. **On Yes** — run the scaffold helper once (it substitutes tokens, creates
167
167
  the project files, adds the root `hot.md` row, and logs the entry):
168
168
  ```
169
- node <pkg-root>/scripts/lib/project-create.mjs --name <slug> --working-dir "$(pwd)"
169
+ node ${CLAUDE_PLUGIN_ROOT}/scripts/lib/project-create.mjs --name <slug> --working-dir "$(pwd)"
170
170
  ```
171
+ To resolve that package root from this guide: if `${CLAUDE_PLUGIN_ROOT}` is
172
+ already an absolute path, use it; otherwise read `pkgRoot` from
173
+ `~/.claude/hypo-pkg.json` (only when non-empty and the script exists under it);
174
+ otherwise use the `hypo@hypomnema` (or legacy `hypomnema@hypomnema`) installPath
175
+ in `~/.claude/plugins/installed_plugins.json`; if none resolve, stop and tell the
176
+ user to run `hypomnema upgrade --apply` or reinstall instead of guessing.
171
177
  Then tell the user: "Created project `<name>` at
172
178
  `~/hypomnema/projects/<name>/`. Edit `index.md` to refine." Do **not** hand-write
173
179
  the five files — the helper keeps substitution and registration consistent.
@@ -219,7 +225,7 @@ ADRs are immutable once accepted. Mark deprecated/superseded but never edit cont
219
225
 
220
226
  Hypomnema v1.1.0 ships an **autonomy score** so you can see whether the wiki is actually being used per session, rather than just trusting that it is.
221
227
 
222
- - **Citation:** when a response uses a wiki page, link it inline as `[[page-slug]]`. The audit script counts citations as one signal of "the wiki was actually consulted".
228
+ - **Citation:** when a response uses a wiki page, link it inline as `[[page-slug]]` to keep it connected in the graph. (The audit score is based on the tool and command usage below, not on these inline citations.)
223
229
  - **Tools that count:** `Grep`, `WebSearch`, `WebFetch`, and any `/hypo:query` / `/hypo:ingest` / `/hypo:feedback` invocation in the transcript.
224
230
  - **Classification (heuristic v0):**
225
231
  - `normal` — at least one search, no missed URL ingests
@@ -229,8 +235,8 @@ Hypomnema v1.1.0 ships an **autonomy score** so you can see whether the wiki is
229
235
  - `staleness-skip` — session older than the audit window (default 30d)
230
236
  - **Where it lives:**
231
237
  - Per-session transcript index: `<hypo-root>/.cache/sessions/index.jsonl` (written by the Stop hook `hypo-session-record.mjs`).
232
- - Fallback source: `~/.claude/projects/<encoded>/*.jsonl` (used when the index is empty — see ADR 0019 if present in your wiki).
233
- - Reports: `journal/weekly/<YYYY-Www>.md` (spec §6.4 SoT), generated by `node scripts/weekly-report.mjs --write`.
238
+ - Fallback source: `~/.claude/projects/<encoded>/*.jsonl` (used when the index is empty).
239
+ - Reports: `journal/weekly/<YYYY-Www>.md` (spec §6.4 SoT), generated by the `/hypo:audit` weekly report flow.
234
240
  - **Definitions:** the 0% / 100% endpoints, the formal score sketch, and open questions live in `pages/observability/_index.md`.
235
241
 
236
242
  Use `/hypo:audit` for a quick read of recent sessions; pass `--write` to commit the weekly report into the wiki.
@@ -21,7 +21,7 @@ tags: [hot-cache, project]
21
21
  ## Key Decisions
22
22
 
23
23
  <!-- Brief pointers to important ADRs or design choices -->
24
- <!-- [[decisions/0001-topic]] — decision summary -->
24
+ <!-- [[decisions/NNNN-topic]] — decision summary -->
25
25
 
26
26
  ## Blockers / Open Questions
27
27
 
@@ -1,256 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * fix-status-verify (CLI) — verify fix→test linkage + ADR-line evidence
4
- * against wiki spec claims.
5
- *
6
- * Phase 1: test-green half (anchors × spec status × runner results).
7
- * Phase 2 (A-sot): manifest validation + manifest↔anchor drift + ADR core
8
- * decision grep against the production corpus. See scripts/lib/fix-manifest.mjs
9
- * and scripts/lib/fix-status-verify.mjs headers.
10
- *
11
- * Usage:
12
- * node scripts/fix-status-verify.mjs [--hypo-dir <path>]
13
- * [--spec <path>]
14
- * [--runner <path>]
15
- * [--test-command "<cmd>"]
16
- * [--json]
17
- *
18
- * Exit 0 if no error-level findings, 1 otherwise.
19
- */
20
-
21
- import { readFileSync, existsSync } from 'node:fs';
22
- import { join, dirname, resolve } from 'node:path';
23
- import { homedir } from 'node:os';
24
- import { spawnSync } from 'node:child_process';
25
- import { fileURLToPath, pathToFileURL } from 'node:url';
26
- import {
27
- parseAnchors,
28
- parseStatus,
29
- parseRunnerOutput,
30
- verifyMatrix,
31
- isReferenceStub,
32
- validateManifest,
33
- checkManifestCoverage,
34
- checkAdrLines,
35
- FIX_MANIFEST,
36
- NO_ADR,
37
- } from './lib/fix-status-verify.mjs';
38
- import { buildCorpusSearch } from './lib/adr-corpus.mjs';
39
-
40
- const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..');
41
-
42
- // Production-code corpus for the ADR-line grep (spec §A amendment 2026-06-07:
43
- // templates/ ships via npm `files`, so prompt-driven fixes are verifiable).
44
- const CORPUS_DIRS = ['scripts', 'hooks', 'commands', 'skills', 'templates'];
45
- // MUST exclude the manifest itself — it holds every adrKeyLine as a literal and
46
- // would self-match, making ADR_LINE_MISSING impossible to ever fire.
47
- const CORPUS_EXCLUDE = ['scripts/lib/fix-manifest.mjs'];
48
-
49
- function parseArgs(argv) {
50
- const out = {
51
- hypoDir: process.env.HYPO_DIR || join(homedir(), 'hypomnema'),
52
- spec: null,
53
- runner: join(REPO, 'tests/runner.mjs'),
54
- testCommand: 'npm test',
55
- json: false,
56
- manifest: null,
57
- };
58
- for (let i = 0; i < argv.length; i++) {
59
- const a = argv[i];
60
- if (a === '--hypo-dir') out.hypoDir = argv[++i];
61
- else if (a === '--spec') out.spec = argv[++i];
62
- else if (a === '--runner') out.runner = argv[++i];
63
- else if (a === '--test-command') out.testCommand = argv[++i];
64
- else if (a === '--manifest') out.manifest = argv[++i];
65
- else if (a === '--json') out.json = true;
66
- else if (a === '--help' || a === '-h') {
67
- printHelp();
68
- process.exit(0);
69
- } else {
70
- console.error(`unknown arg: ${a}`);
71
- process.exit(2);
72
- }
73
- }
74
- if (!out.spec) {
75
- out.spec = join(out.hypoDir, 'projects/hypomnema/spec-v1.2.md');
76
- }
77
- return out;
78
- }
79
-
80
- function printHelp() {
81
- console.log(
82
- [
83
- 'fix-status-verify — Phase 1 (test-green half) of learned_behavior #6',
84
- '',
85
- 'Options:',
86
- ' --hypo-dir <path> Wiki root (default: $HYPO_DIR or ~/hypomnema)',
87
- ' --spec <path> Override spec-v1.2.md path',
88
- ' --runner <path> Override tests/runner.mjs path',
89
- ' --test-command "<cmd>" Test invocation (default: "npm test")',
90
- ' --json Emit machine-readable JSON report',
91
- '',
92
- 'Exit 0 if no error findings, 1 otherwise.',
93
- '',
94
- 'NOTE: The default --spec is a `type: reference` redirect stub (the real',
95
- 'spec moved to archive/). Running without --spec fails with STUB_SPEC by',
96
- 'design — pass --spec <real spec> to verify against actual claims.',
97
- '',
98
- 'Phase 2 (A-sot): also greps each manifest adrKeyLine against the',
99
- 'production corpus (scripts/ hooks/ commands/ skills/ templates/) and',
100
- 'checks manifest↔anchor drift. NO_ADR rows skip the grep (test-green only).',
101
- ].join('\n'),
102
- );
103
- }
104
-
105
- function runTests(testCommand) {
106
- // Parse simple command (no shell metacharacters supported in args; this is
107
- // a maintainer tool, not a security boundary).
108
- const parts = testCommand.split(/\s+/).filter(Boolean);
109
- const [cmd, ...args] = parts;
110
- const result = spawnSync(cmd, args, {
111
- cwd: REPO,
112
- encoding: 'utf-8',
113
- env: { ...process.env, FORCE_COLOR: '0' },
114
- maxBuffer: 64 * 1024 * 1024,
115
- });
116
- return {
117
- stdout: result.stdout || '',
118
- stderr: result.stderr || '',
119
- exitCode: result.status,
120
- };
121
- }
122
-
123
- function formatFinding(f) {
124
- const icon = f.level === 'error' ? '✗' : '⚠';
125
- // Some findings are not tied to a specific fix # (STUB_SPEC,
126
- // TEST_RUN_NONZERO_EXIT). Only render the `fix #N` segment when present so
127
- // they don't print `fix #undefined`.
128
- const ref = f.fixNum != null ? ` fix #${f.fixNum}` : '';
129
- return ` ${icon} [${f.class}]${ref}` + (f.testName ? ` (${f.testName})` : '') + `: ${f.detail}`;
130
- }
131
-
132
- async function main() {
133
- const opts = parseArgs(process.argv.slice(2));
134
-
135
- // Manifest source: built-in code constant by default (ADR 0036). --manifest
136
- // <path.mjs> overrides for tests, which inject a fixture manifest matching
137
- // their synthetic fixes so the real manifest does not couple to fixtures.
138
- let manifest = FIX_MANIFEST;
139
- if (opts.manifest) {
140
- if (!existsSync(opts.manifest)) {
141
- console.error(`manifest not found: ${opts.manifest}`);
142
- process.exit(2);
143
- }
144
- const mod = await import(pathToFileURL(resolve(opts.manifest)).href);
145
- manifest = mod.FIX_MANIFEST;
146
- }
147
-
148
- if (!existsSync(opts.spec)) {
149
- console.error(`spec not found: ${opts.spec}`);
150
- console.error('hint: pass --hypo-dir <path> or set $HYPO_DIR');
151
- process.exit(2);
152
- }
153
- if (!existsSync(opts.runner)) {
154
- console.error(`runner not found: ${opts.runner}`);
155
- process.exit(2);
156
- }
157
-
158
- const specText = readFileSync(opts.spec, 'utf-8');
159
- const runnerText = readFileSync(opts.runner, 'utf-8');
160
-
161
- const anchors = parseAnchors(runnerText);
162
- const status = parseStatus(specText);
163
- const specIsStub = isReferenceStub(specText);
164
-
165
- if (!opts.json) {
166
- console.log(`fix-status-verify (Phase 1)`);
167
- console.log(` spec: ${opts.spec}`);
168
- console.log(` runner: ${opts.runner}`);
169
- console.log(` ${status.size} positive status claim(s), ${anchors.size} anchor(s)`);
170
- console.log(` running: ${opts.testCommand}`);
171
- }
172
-
173
- const testRun = runTests(opts.testCommand);
174
- const testResults = parseRunnerOutput(testRun.stdout + '\n' + testRun.stderr);
175
-
176
- if (!opts.json) {
177
- const passes = [...testResults.values()].filter((v) => v === 'pass').length;
178
- const fails = [...testResults.values()].filter((v) => v === 'fail').length;
179
- console.log(` test run: ${passes} pass, ${fails} fail (exit ${testRun.exitCode})`);
180
- }
181
-
182
- const matrixResult = verifyMatrix({ anchors, status, testResults, specIsStub });
183
- const findings = [...matrixResult.findings];
184
-
185
- // Phase 2 (A-sot): manifest validation + ADR-line grep. validateManifest and
186
- // checkAdrLines are spec-independent (manifest/code health) and run always;
187
- // checkManifestCoverage keys off the spec status (a no-op under STUB_SPEC,
188
- // where status is empty).
189
- const needsCorpus = manifest.some((r) => r.adrKeyLine !== NO_ADR);
190
- const adrSearch = needsCorpus
191
- ? buildCorpusSearch({ repoRoot: REPO, includeDirs: CORPUS_DIRS, excludePaths: CORPUS_EXCLUDE })
192
- : () => false;
193
- const adrExists = (adrPath) => existsSync(join(opts.hypoDir, 'projects/hypomnema', adrPath));
194
- findings.push(...validateManifest(manifest));
195
- findings.push(...checkManifestCoverage({ manifest, anchors, status }));
196
- findings.push(...checkAdrLines({ manifest, searchFn: adrSearch, adrExistsFn: adrExists }));
197
-
198
- // CLI-level error: if the test command itself exited nonzero, the test run
199
- // is not green even if the anchored tests happen to all pass in the parsed
200
- // output. Surface as a synthetic error finding so `ok` flips false.
201
- if (testRun.exitCode !== 0) {
202
- findings.push({
203
- level: 'error',
204
- class: 'TEST_RUN_NONZERO_EXIT',
205
- detail: `test command "${opts.testCommand}" exited ${testRun.exitCode}`,
206
- exitCode: testRun.exitCode,
207
- });
208
- }
209
- const ok = !findings.some((f) => f.level === 'error') && testRun.exitCode === 0;
210
-
211
- const MANDATORY_NOTE =
212
- 'test-linkage + green + ADR-line grep (Phase 2): manifest evidence checked against production corpus';
213
-
214
- if (opts.json) {
215
- console.log(
216
- JSON.stringify(
217
- {
218
- ok,
219
- spec: opts.spec,
220
- runner: opts.runner,
221
- statusClaims: status.size,
222
- anchorCount: anchors.size,
223
- testsRan: testResults.size,
224
- testExitCode: testRun.exitCode,
225
- findings,
226
- note: MANDATORY_NOTE,
227
- },
228
- null,
229
- 2,
230
- ),
231
- );
232
- } else {
233
- const errors = findings.filter((f) => f.level === 'error');
234
- const warns = findings.filter((f) => f.level === 'warn');
235
- if (errors.length === 0 && warns.length === 0) {
236
- console.log(` ✓ all ${status.size} claimed-merged fix(es) verified`);
237
- } else {
238
- if (errors.length) {
239
- console.log(`\nerrors (${errors.length}):`);
240
- for (const f of errors) console.log(formatFinding(f));
241
- }
242
- if (warns.length) {
243
- console.log(`\nwarnings (${warns.length}):`);
244
- for (const f of warns) console.log(formatFinding(f));
245
- }
246
- }
247
- console.log(`\n(${MANDATORY_NOTE})`);
248
- }
249
-
250
- process.exit(ok ? 0 : 1);
251
- }
252
-
253
- main().catch((e) => {
254
- console.error(e);
255
- process.exit(2);
256
- });