hypomnema 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +46 -0
  4. package/README.ko.md +8 -6
  5. package/README.md +8 -6
  6. package/commands/crystallize.md +32 -6
  7. package/commands/graph.md +7 -4
  8. package/commands/lint.md +8 -1
  9. package/commands/query.md +6 -4
  10. package/commands/resume.md +1 -0
  11. package/commands/verify.md +12 -1
  12. package/docs/ARCHITECTURE.md +26 -12
  13. package/docs/CONTRIBUTING.md +15 -6
  14. package/hooks/hypo-compact-guard.mjs +126 -23
  15. package/hooks/hypo-cwd-change.mjs +20 -19
  16. package/hooks/hypo-first-prompt.mjs +31 -16
  17. package/hooks/hypo-lookup.mjs +10 -5
  18. package/hooks/hypo-session-start.mjs +180 -2
  19. package/hooks/hypo-shared.mjs +410 -83
  20. package/hooks/hypo-web-fetch-ingest.mjs +9 -13
  21. package/package.json +2 -1
  22. package/scripts/doctor.mjs +32 -3
  23. package/scripts/graph.mjs +22 -2
  24. package/scripts/init.mjs +5 -1
  25. package/scripts/lib/crystallize-args.mjs +38 -2
  26. package/scripts/lib/crystallize-close-apply.mjs +745 -450
  27. package/scripts/lint.mjs +242 -39
  28. package/scripts/query.mjs +22 -2
  29. package/scripts/resume.mjs +177 -2
  30. package/scripts/upgrade.mjs +2 -2
  31. package/scripts/verify.mjs +22 -2
  32. package/templates/SCHEMA.md +23 -1
  33. package/templates/hypo-automation.md +4 -2
  34. package/templates/hypo-config.md +1 -1
  35. package/templates/hypo-guide.md +1 -1
  36. package/skills/crystallize/SKILL.md +0 -189
  37. package/skills/graph/SKILL.md +0 -58
  38. package/skills/ingest/SKILL.md +0 -107
  39. package/skills/lint/SKILL.md +0 -59
  40. package/skills/query/SKILL.md +0 -62
  41. package/skills/verify/SKILL.md +0 -96
@@ -22,12 +22,32 @@ import { loadHypoIgnore, isScanIgnored } from './lib/hypo-ignore.mjs';
22
22
 
23
23
  // ── arg parsing ──────────────────────────────────────────────────────────────
24
24
 
25
+ const ALLOWED_FLAGS = ['--hypo-dir=<path>', '--file=<path>', '--json'];
26
+
25
27
  function parseArgs(argv) {
26
28
  const args = { hypoDir: null, file: null, json: false };
27
29
  for (const arg of argv.slice(2)) {
28
- if (arg.startsWith('--hypo-dir=')) args.hypoDir = expandHome(arg.slice(11));
29
- else if (arg.startsWith('--file=')) args.file = expandHome(arg.slice(7));
30
+ if (arg.startsWith('--hypo-dir=')) {
31
+ // See lint.mjs's parseArgs for why an empty raw value (--hypo-dir=,
32
+ // or --hypo-dir="$VAULT" with VAULT unset) is rejected instead of
33
+ // falling through to the default-resolution branch below.
34
+ const raw = arg.slice(11);
35
+ if (!raw) {
36
+ console.error(`verify.mjs: --hypo-dir requires a non-empty value (got '${arg}')`);
37
+ console.error(`verify.mjs accepts: ${ALLOWED_FLAGS.join(', ')}`);
38
+ process.exit(2);
39
+ }
40
+ args.hypoDir = expandHome(raw);
41
+ } else if (arg.startsWith('--file=')) args.file = expandHome(arg.slice(7));
30
42
  else if (arg === '--json') args.json = true;
43
+ else {
44
+ // No positional arguments here either, so an unmatched arg is rejected
45
+ // rather than dropped. See lint.mjs's parseArgs for the background
46
+ // this closes.
47
+ console.error(`verify.mjs: unrecognized argument '${arg}'`);
48
+ console.error(`verify.mjs accepts: ${ALLOWED_FLAGS.join(', ')}`);
49
+ process.exit(2);
50
+ }
31
51
  }
32
52
  if (!args.hypoDir) args.hypoDir = resolveHypoRoot();
33
53
  return args;
@@ -2,7 +2,7 @@
2
2
  title: Wiki Schema
3
3
  type: schema
4
4
  updated: YYYY-MM-DD
5
- version: 2.1
5
+ version: 2.2
6
6
  ---
7
7
 
8
8
  # Wiki Schema
@@ -85,6 +85,16 @@ tags: [tag1, tag2]
85
85
  ---
86
86
  ```
87
87
 
88
+ **`updated` is the day the content or structure changed**, not the day the file was
89
+ last written. Adding a backlink, fixing a typo, or reflowing a line does not move it.
90
+ Staleness comparisons read this field, so a date bumped for a cross-link makes the
91
+ page look fresher than its claims are.
92
+
93
+ **Nothing enforces this.** `lint` checks only that the field exists (W3), and `--fix`
94
+ fills a missing one with today's date, which records when the gap was closed rather
95
+ than when the content changed. The rule holds only where whoever edits the page
96
+ applies it.
97
+
88
98
  Optional fields (add as needed):
89
99
 
90
100
  ```yaml
@@ -95,8 +105,20 @@ visibility_scope: shared | machine:<device>
95
105
  source: <slug or URL>
96
106
  verify_by: <question to re-check at next review>
97
107
  verify_by_date: YYYY-MM-DD
108
+ sources_consulted: [slug1, slug2]
98
109
  ```
99
110
 
111
+ `sources_consulted` belongs on a `synthesis` page and names the pages it condenses.
112
+ `lint` reads it: when the newest `updated` among those sources is later than the
113
+ synthesis's own `updated`, the page is reported as stale (W15), so a synthesis that
114
+ has not absorbed what its sources learned since surfaces without anyone diffing dates
115
+ by hand. A bare slug is enough when it is unique. Resolution covers `pages/`,
116
+ `projects/` and `journal/`, which is narrower than what a wikilink target
117
+ accepts, so a name outside those (a directory, a `sources/` file, a path into
118
+ a code repo) does not resolve. An unresolved name is left out of the staleness
119
+ comparison and reported separately (W16), because a name this field cannot
120
+ resolve is either a typo or a value the field is not meant to hold.
121
+
100
122
  `visibility_scope` is a different axis from `scope`: `scope` is memory lifetime,
101
123
  `visibility_scope` is where a page may surface. Omitting it means `shared` (the
102
124
  implicit default of every pre-existing page), so lookup/query/injection stay
@@ -31,8 +31,10 @@ All 15 hooks registered in `hooks/hooks.json` are listed below, in registration
31
31
  | `hypo-session-record.mjs` | `Stop` | Appends the completed session to the session index |
32
32
  | `hypo-auto-commit.mjs` | `Stop` | Stages, commits, and pushes this session's touched wiki paths |
33
33
  | `hypo-auto-minimal-crystallize.mjs` | `Stop` | Blocks `Stop` when the session did substantial work, the user signalled wrap-up, and no close was recorded. A substantial session with no close signal is let through |
34
- | `hypo-cwd-change.mjs` | `CwdChanged` | Re-injects the matching project's `hot.md` when the working directory changes mid-session |
35
- | `hypo-file-watch.mjs` | `FileChanged` | Re-injects any vault file changed on disk outside the session, once it passes the ignore and visibility filters |
34
+ | `hypo-cwd-change.mjs` | `CwdChanged` | Builds `additionalContext` with the matching project's `hot.md` when the working directory changes mid-session, but `CwdChanged` has no field Claude Code forwards to the model, so this output does not currently reach Claude |
35
+ | `hypo-file-watch.mjs` | `FileChanged` | Builds `additionalContext` for a changed vault file once it passes the ignore and visibility filters. Nothing in this package registers watch paths, so the event has no trigger here, and `FileChanged` output does not reach Claude either way |
36
+
37
+ This file is copied into a vault only at `init` time, and no update channel pushes a later fix here into a vault that already exists. So this copy can be older than the shipped one. `hooks/hooks.json` in the package is the source of truth for which hook runs on which event; when this table disagrees with it, the table is what went stale.
36
38
 
37
39
  Two hooks reach the network; the rest compute locally. There are two separate kinds of
38
40
  network traffic here, and only one of them can be turned off.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: Hypomnema Config
3
3
  type: config
4
- version: "1.8.0"
4
+ version: "1.8.2"
5
5
  created: YYYY-MM-DD
6
6
  ---
7
7
 
@@ -164,7 +164,7 @@ Add to root `hot.md` active projects table.
164
164
 
165
165
  #### Auto-project offer
166
166
 
167
- When SessionStart / CwdChanged injects a line like
167
+ When SessionStart injects a line like
168
168
  `[WIKI: cwd '<name>'에 매칭되는 프로젝트가 없습니다. 자동 생성할까요? (Y/n)]`,
169
169
  the current working directory is a real project (git repo + a project marker
170
170
  like `package.json`) that has no matching wiki project. **Act on it — do not
@@ -1,189 +0,0 @@
1
- ---
2
- description: Close a session by capturing what happened into the wiki (steps 1-6), and on request consolidate scattered notes into stable pages (steps 7-11). Use when the user explicitly signals session end, asks to save or crystallize the session, or before a /compact. Task completion alone is not a close signal.
3
- ---
4
-
5
- You are running `/hypo:crystallize`. The command serves two modes (spec §5.2.7 / §8.3):
6
-
7
- 1. **Session close (steps 1-6)** — walk the 5 mandatory memory files plus open-questions (conditional). `/compact` no longer waits on this; `--check-session-close` is the actual go/no-go signal for calling the session closed.
8
- 2. **Synthesis (steps 7-11)** — surface tag clusters, orphan pages, and drafts that are ready to consolidate.
9
-
10
- When invoked to close a session — via an explicit close signal ("세션 종료", "wrap up"), an accepted proactive-offer [세션 마무리], or `/compact` — run the session-close checklist first. Task completion alone does not put you in close mode. The synthesis scan only runs after close is confirmed and the user agrees.
11
-
12
- ## What this does
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 `/hypo:crystallize` in `--apply-session-close --payload=<path> --session-id=<id>` mode, which runs the lint gate automatically, **scoped to the files it writes** (debt elsewhere is a non-blocking notice). **`--session-id` is required.** Before writing a byte, the apply resolves that session's transcript and refuses the whole close, exit 1 and nothing on disk, unless the **user** asked for it (`session-id-required` / `transcript-unresolved` / `no-user-close-signal`). A refusal is the answer, not an obstacle: ask the user, and re-run only if they say yes. `--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 `hypo-personal-check.mjs` PreCompact hook. That hook itself no longer blocks `/compact`; it only turns a red result into a `systemMessage`. So a green check here is not "the gate that will stop `/compact`" but the same signal the hook would have surfaced, checked ahead of time: no close-file gap, lint blocker, stale design-history, or feedback-projection issue needs a human fix, which is the practical bar for declaring 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 even at that lower bar: the live PreCompact run can still differ on a context-≥70% prompt, `HYPO_SKIP_GATE`, or a transcript-scoped lint error the check did not see, and none of that differs `/compact` itself, which proceeds either way.
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
-
17
- ---
18
-
19
- ## Step 1 — Locate package root
20
-
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 `/hypo:upgrade` on a plugin install) or reinstall instead of guessing the cache layout.
22
-
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 via `HYPO_DIR` → `hypo-config.md` scan → `~/hypomnema`.
24
-
25
- ---
26
-
27
- ## Step 2 — Run crystallize scan
28
-
29
- ```bash
30
- node ${CLAUDE_PLUGIN_ROOT}/scripts/crystallize.mjs \
31
- [--hypo-dir="<path>"] \
32
- [--min-group=<n>] \
33
- [--json]
34
- ```
35
-
36
- Options:
37
- - `--min-group=<n>` — minimum pages per tag group to report (default: 2)
38
- - `--json` — output results as JSON
39
-
40
- Show the output verbatim.
41
-
42
- ---
43
-
44
- ## Step 3 — Session-close checklist (if triggered at session end)
45
-
46
- If `/hypo:crystallize` was invoked as a session-close action, run through this checklist before synthesizing. The mechanical checklist items (1–6 below) proceed automatically without confirmation unless the user has not said "auto"; the advisory reflections that precede them (#41~#44) always surface to the user for confirmation — they are recommendations, never auto-actions.
47
-
48
- ### Advisory reflections (run before the checklist below — advisory only)
49
-
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
-
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
- - **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, or 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. This check carries no `decisions/` directory precondition: run it whether or not that directory exists.
54
- - **design-history staleness check (#42).** Two branches, so a stale W8 never blocks a clean close: (a) if this session changed design decisions that `projects/<name>/design-history.md` does not yet reflect (including background, tradeoff, or differentiation shifts that are below ADR level but still belong in the ledger), recommend appending to it now: W8 flags this mechanically, and an active-project W8 still counts as a `--check-session-close` blocker even though the PreCompact hook no longer stops `/compact` on it, so append before you commit, not after the check flags it. **If the file does not exist yet and this session had a design change, recommend creating it now** with that change as the first entry; lint separately flags a missing-but-needed file as W14, a warning that never blocks. (b) only if this session made **no** design change at all does the `ADR 없음` marker from #41 exempt the entry from W8; do **not** touch design-history, and do not create the file just to satisfy this branch's check. Caution: `ADR 없음` means "no design change," which is a stricter bar than "no ADR-level decision." A session with a sub-ADR design shift should take branch (a) and append; writing `ADR 없음` there would suppress the W8 nudge it actually needs. Never auto-write the file yourself in either branch: recommend it, and let the user decide.
55
- - **Ingest check (#43)** — Did this session consume trustworthy external knowledge (a fetched URL, official docs, or code you verified directly)? If so, recommend running `/hypo:ingest` to capture it under `sources/`. Proceed only on the user's confirmation.
56
-
57
- When uncertain, surface the question rather than skip it. None of the four blocks the close or writes on its own.
58
-
59
- 1. **session-state.md** — update `projects/<name>/session-state.md` with the next tasks list (what to tackle first next time).
60
- 2. **hot.md (project)** — update `projects/<name>/hot.md` with a session snapshot: what changed and decisions made. Keep under 500 words. Do not put next-step tasks here; those belong in session-state.md.
61
- 3. **hot.md (root)** — update `<wiki-root>/hot.md` active-projects pointer table: set the `Last Session` date for this project to today.
62
- 4. **session-log** — append a session entry to `projects/<name>/session-log/YYYY-MM-DD.md` (daily shard; the apply path creates today's file with seeded frontmatter if it does not exist yet).
63
- 5. **open-questions** — only if `pages/open-questions.md` exists and questions were raised or resolved this session: move resolved ones out; add newly raised ones. Skip if unchanged.
64
-
65
- > Do **not** hand-write the root `log.md` session entry, and do **not** put a `log` field in the apply payload. The `## [date] session | <project>` entry is a derivable artifact: `--apply-session-close` calls `deriveRootLogEntries` after writing the session-log, reconstructing the canonical line from the session-log heading. (Supply `log` only for a deliberately custom line; it is now an optional payload field.)
66
-
67
- After completing the checklist, verify it before reporting:
68
-
69
- ```bash
70
- node ${CLAUDE_PLUGIN_ROOT}/scripts/crystallize.mjs --check-session-close [--hypo-dir="<path>"]
71
- ```
72
-
73
- This runs the **full** set of checks the `hypo-personal-check.mjs` PreCompact
74
- hook itself runs, via the shared `precompactGateStatus`: close files
75
- (`missing` / `stale`) plus lint blockers, stale design-history, and feedback
76
- projection over-cap/conflict. Fix every `✗` it reports and re-run until it
77
- prints **"Compact-ready"**, that is the signal the session is closed. A
78
- close-files-only pass is not enough to earn that signal; the other checks
79
- still gate it here, even though the live `/compact` no longer blocks on any
80
- of them (that hook now only turns a red result into a `systemMessage`).
81
-
82
- Optional `--project=<slug>` narrows the check to ONE project (close status plus
83
- lint scope). It is a project-scoped **diagnostic**, not the compact-ready
84
- signal. A green `--project` result (JSON `scope: "project"`) means only that
85
- project is close-complete; another today-active project can still block
86
- `/compact`. Use the plain `--check-session-close` (no `--project`) for the
87
- go/no-go close signal.
88
-
89
- **When using `--apply-session-close --session-id=<id>`** (the payload-driven
90
- path), the `--session-id` must be the main conversation's session id. Do NOT
91
- extract it from a background task or Agent output path (e.g., a UUID from
92
- `/tmp/.../<uuid>/tasks/...`). Such a UUID is a background task id, not the
93
- main conversation id. Passing the wrong id causes `markerWritten: false` with
94
- `markerSkipReason: "transcript-unresolved"` or `"no-user-close-signal"`: the
95
- 5 mandatory files are written (ok: true) but the Stop-chain marker is withheld,
96
- so the session is not actually closed and the Stop hook re-prompts. The correct
97
- id comes from the `[WIKI_AUTOCLOSE]` block reason or the injected
98
- `$CLAUDE_CODE_SESSION_ID` (accept the legacy spelling via
99
- `${CLAUDE_CODE_SESSION_ID:-$CLAUDE_SESSION_ID}`).
100
-
101
- Once it passes, report each item with ✓:
102
- - ✓ session-state.md
103
- - ✓ hot.md (project + root)
104
- - ✓ session-log entry
105
- - ✓ open-questions (or skipped if unchanged)
106
- - ✓ log.md entry
107
- - **marker written?** (required): 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 branches below.
108
-
109
- If `markerWritten: true`, ask: "Session closed. Would you like to also run knowledge synthesis now, or stop here?"
110
-
111
- **If `ok: false` with an authority `reason`, the close did not happen at all.** No file was written, nothing was committed. Do not report a partial close, and do not write the files by hand to get around it.
112
-
113
- - `session-id-required`: `--session-id` was omitted. Pass the main conversation's id and re-run.
114
- - `transcript-unresolved`: the id resolved no transcript, so it is most likely a background-task or Agent-thread uuid rather than the main conversation's. Get the right one and re-run.
115
- - `no-user-close-signal`: the transcript is this session's, and the user never asked to close in wording the gate recognizes (e.g. "세션 마무리까지 진행해줘" falls outside the close-signal set). Re-running the same id changes nothing. Confirm intent once with `AskUserQuestion` (header "세션", one option **세션 마무리**); if the user picks it, that answer becomes a recognized close signal, so the same command now applies everything: writes, commit, and marker. If the user declines, the session stays open and nothing is written. Do NOT change the close-signal matcher.
116
-
117
- **If `ok: false` with `stage: "session-id-mismatch"`,** the payload carries a `sessionId` that is not the `--session-id` you passed, so this file was authored for a different session (a reused or overwritten temp path). Nothing was written. Do not force it: rebuild the payload for THIS session (write it to a session-scoped path, e.g. `/tmp/hypo-session-close-<session-id>.json`) and re-run.
118
-
119
- If the apply succeeded but `markerWritten: false`, branch on `markerSkipReason` (`compact-gate-not-ok`, `commit-failed: …`, `marker-did-not-land`): surface it verbatim and resolve the underlying blocker before re-running.
120
-
121
- ### If the close came back `ok: false, stage: "proposal-pending"`
122
-
123
- A concurrent session wrote one of the pages this close wants to replace, so the close
124
- withheld the bytes rather than clobber that session's work, and parked them. Nothing is
125
- lost and nothing is broken. The close is waiting on a decision only the user makes:
126
- whether to replace the other session's bytes.
127
-
128
- Do NOT write the pages with the Write tool and mark the session closed. That path skips
129
- the store entirely and silently destroys the other session's edits. Drive it out properly:
130
-
131
- 1. **Look at `proposals`.** If the array is EMPTY, no page was parked. The only conflicts
132
- were append-lock timeouts, which the next close re-reads and re-appends by itself. Just
133
- re-run the same close command.
134
- 2. **`hypomnema proposal challenge --session-id <id> --ids <id,...>`** with the ids from
135
- `proposals`. It re-reads each page from disk and prints the diff between what is there
136
- now and what this close wants to write.
137
- 3. **Show the user those diffs** and tell them plainly what would be overwritten. Then give
138
- them the approval line the command printed, and ask them to type it:
139
-
140
- apply-proposals <nonce>
141
-
142
- They have to TYPE it. A click on `AskUserQuestion` does not approve an overwrite (you
143
- author the option labels, so a click cannot prove they approved this specific write),
144
- and the command refuses it. If they would rather keep the other session's page, run
145
- `hypomnema proposal discard <id>` instead and re-run the close.
146
- 4. **`hypomnema proposal resolve --session-id <id>`** once they have typed it. It verifies
147
- the approval in the transcript, checks the pages have not moved since they saw the diff,
148
- and writes them.
149
- 5. **Re-run the close.** `resolve` writes the pages; it does NOT close the session. The
150
- re-run skips what already landed and finishes the close.
151
- 6. **Check `markerWritten` again** before you say the session is closed. It is never
152
- automatic.
153
-
154
- ---
155
-
156
- ## Step 4 — Pick a synthesis target
157
-
158
- Present the top candidates from the script output:
159
- - Tag clusters with the most pages
160
- - Long-standing orphans
161
- - Pages marked `status: draft`
162
-
163
- Ask: "Which of these would you like to crystallize now? (or 'all' / 'skip')"
164
-
165
- ---
166
-
167
- ## Step 5 — Synthesize
168
-
169
- For the chosen target:
170
-
171
- 1. Read all pages in the cluster.
172
- 2. Write a new synthesis page at `pages/syntheses/<slug>.md` with:
173
-
174
- ```yaml
175
- ---
176
- title: <synthesis title>
177
- type: synthesis
178
- tags: [<shared tags>]
179
- updated: <today YYYY-MM-DD>
180
- evidence_strength: inferred
181
- ---
182
- ```
183
-
184
- 3. Cross-link all source pages back to the synthesis with `[[wikilink]]`.
185
- 4. Add the synthesis to `index.md` under `## Pages — Syntheses`.
186
-
187
- ---
188
-
189
- > **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]].
@@ -1,58 +0,0 @@
1
- ---
2
- description: Generate a wikilink dependency graph from wiki pages (json, mermaid, or dot). Use when the user asks to visualize wiki structure, find orphan or hub pages, or map how notes link together.
3
- ---
4
-
5
- You are running `/hypo:graph`. Build a wikilink dependency graph from all pages under `pages/` and `projects/` and output it in the requested format.
6
-
7
- ## What this produces
8
-
9
- - **json** (default) — adjacency list with in/out-degree counts per node, sorted by total edges
10
- - **mermaid** — `graph TD` Mermaid diagram (paste into any Mermaid renderer)
11
- - **dot** — Graphviz DOT format (pipe to `dot -Tsvg` for an SVG)
12
-
13
- ---
14
-
15
- ## Step 1 — Locate package root
16
-
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 `/hypo:upgrade` on a plugin install) or reinstall instead of guessing the cache layout.
18
-
19
- 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`.
20
-
21
- ---
22
-
23
- ## Step 2 — Ask for output format (optional)
24
-
25
- If the user did not specify a format, ask:
26
-
27
- > "Output format? (json / mermaid / dot) [json]"
28
-
29
- Default: `json`
30
-
31
- Optionally ask:
32
-
33
- > "Minimum edges to include a node? (0 = all) [0]"
34
-
35
- ---
36
-
37
- ## Step 3 — Run graph
38
-
39
- ```bash
40
- node ${CLAUDE_PLUGIN_ROOT}/scripts/graph.mjs \
41
- [--hypo-dir="<path>"] \
42
- [--format=json|mermaid|dot] \
43
- [--min-edges=<n>]
44
- ```
45
-
46
- ---
47
-
48
- ## Step 4 — Present results
49
-
50
- - **json**: summarise the top 10 most-connected nodes (by `in + out`), then offer to show the full JSON.
51
- - **mermaid**: wrap the output in a fenced code block tagged `mermaid` so it renders inline.
52
- - **dot**: wrap the output in a fenced code block tagged `dot` and suggest the user pipe it to `dot -Tsvg -o graph.svg`.
53
-
54
- If the graph has 0 edges, note that no `[[wikilinks]]` were found between pages.
55
-
56
- ---
57
-
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]].
@@ -1,107 +0,0 @@
1
- ---
2
- description: Add an external source (URL, doc, article, command output) to the wiki and synthesize a citable summary page. Use when the user shares a source to capture, asks to ingest something, or wants reliable external knowledge saved for reuse.
3
- ---
4
-
5
- You are running `/hypo:ingest`. Add a new source document to `sources/` and create (or update) its corresponding `source-summary` page under `pages/`.
6
-
7
- ## What this does
8
-
9
- - Checks which files in `sources/` are missing a `source-summary` page
10
- - Reports pages that reference a source file that does not exist in `sources/`
11
- - After the script runs, guides you to synthesize a summary for any un-ingested source
12
-
13
- ---
14
-
15
- ## Step 1 — Locate package root
16
-
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 `/hypo:upgrade` on a plugin install) or reinstall instead of guessing the cache layout.
18
-
19
- 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`.
20
-
21
- ---
22
-
23
- ## Step 2 — Run ingest status check
24
-
25
- ```bash
26
- node ${CLAUDE_PLUGIN_ROOT}/scripts/ingest.mjs [--hypo-dir="<path>"] [--json]
27
- ```
28
-
29
- Options:
30
- - `--json` — output results as JSON (useful for tooling)
31
-
32
- Show the output verbatim.
33
-
34
- ---
35
-
36
- ## Step 3 — Privacy guard (`.hypoignore`)
37
-
38
- Before touching any source content, refuse to ingest secrets (`.env`, SSH keys, credentials). Run the guard for **both** the input path and the destination path:
39
-
40
- 1. **If the user provided a file path**, check it (use an absolute path):
41
-
42
- ```bash
43
- node ${CLAUDE_PLUGIN_ROOT}/scripts/ingest.mjs [--hypo-dir="<path>"] --check="<absolute-input-path>"
44
- ```
45
-
46
- 2. **Always** check the destination `sources/<slug>.<ext>`:
47
-
48
- ```bash
49
- node ${CLAUDE_PLUGIN_ROOT}/scripts/ingest.mjs [--hypo-dir="<path>"] --check="sources/<slug>.<ext>"
50
- ```
51
-
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.
53
-
54
- ---
55
-
56
- ## Step 4 — Handle the source file
57
-
58
- **If the user provided a file or URL to ingest:**
59
-
60
- 1. Copy or download the source into `<wiki-root>/sources/<slug>.<ext>` (e.g., `sources/2026-05-07-article-title.md`).
61
- 2. Confirm the file is now present.
62
-
63
- **If no file was provided:**
64
-
65
- List un-ingested sources from the script output and ask which one to process now.
66
-
67
- ---
68
-
69
- ## Step 5 — Synthesize a source-summary page
70
-
71
- For the chosen source, read its content and create `pages/<slug>.md` with the following frontmatter:
72
-
73
- ```yaml
74
- ---
75
- title: <descriptive title>
76
- type: source-summary
77
- source: <filename>
78
- tags: [<relevant tags>]
79
- updated: <today YYYY-MM-DD>
80
- evidence_strength: direct # or inferred
81
- ---
82
- ```
83
-
84
- Then write a concise summary:
85
- - Key ideas (bullet list)
86
- - Why this source matters to the wiki
87
- - Any open questions or follow-up items
88
-
89
- Cross-reference existing pages with `[[wikilink]]` syntax where relevant.
90
-
91
- ---
92
-
93
- ## Step 6 — Update log.md
94
-
95
- Append an ingest entry to `<wiki-root>/log.md`:
96
-
97
- ```
98
- ## <YYYY-MM-DD> ingest — <slug>
99
-
100
- - source: sources/<filename>
101
- - summary: pages/<slug>.md
102
- - tags: <tags>
103
- ```
104
-
105
- ---
106
-
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]].
@@ -1,59 +0,0 @@
1
- ---
2
- description: Lint wiki pages for frontmatter errors and broken wikilinks. Use when the user asks to check or validate the wiki health, before a commit, or after bulk edits or renames.
3
- ---
4
-
5
- You are running `/hypo:lint`. Validate all wiki pages for frontmatter correctness and broken `[[wikilink]]` references.
6
-
7
- ## What this checks
8
-
9
- - Every `.md` file under `pages/` and `projects/` should have `---` frontmatter (missing frontmatter is a warning, not an error)
10
- - Required fields: `title`, `type`
11
- - `type` must be one of the recognised values (concept, source-summary, learning, adr, …)
12
- - `updated` field should be present
13
- - All `[[wikilinks]]` must resolve to an existing page slug
14
-
15
- ---
16
-
17
- ## Step 1 — Locate package root
18
-
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 `/hypo:upgrade` on a plugin install) or reinstall instead of guessing the cache layout.
20
-
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
-
23
- ---
24
-
25
- ## Step 2 — Run lint
26
-
27
- ```bash
28
- node ${CLAUDE_PLUGIN_ROOT}/scripts/lint.mjs [--hypo-dir="<path>"] [--json] [--fix]
29
- ```
30
-
31
- Options:
32
- - `--json` — output results as JSON (useful for tooling)
33
- - `--fix` — auto-add missing `updated` field (safe repairs only; no other fields are modified)
34
-
35
- Show the output verbatim.
36
-
37
- ---
38
-
39
- ## Step 3 — Interpret results
40
-
41
- - `✓ No lint issues found` — wiki is clean
42
- - `✗ <file>: <message>` — error (missing required field or malformed frontmatter); must be fixed
43
- - `⚠ <file>: <message>` — warning (unknown type, missing `updated`, broken link); worth fixing
44
-
45
- A non-zero exit code means at least one **error** was found (warnings alone do not produce a non-zero exit code).
46
-
47
- ---
48
-
49
- ## Step 4 — Offer to fix
50
-
51
- For **broken wikilinks**: list the affected files and ask if the user wants help correcting the links now.
52
-
53
- For **missing `updated`**: suggest running with `--fix` to auto-add `updated: <today>` to each affected page's frontmatter. Note: `--fix` only repairs files that already have a valid, closed frontmatter block — files with no frontmatter or malformed frontmatter are skipped.
54
-
55
- For **missing required fields** (`title`, `type`): open the affected files and help the user fill them in.
56
-
57
- ---
58
-
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]].
@@ -1,62 +0,0 @@
1
- ---
2
- description: Search the wiki by keyword and synthesize an answer from the relevant pages. Use when the user asks what the wiki knows about a topic, wants to recall a past decision, or needs prior context before starting work.
3
- ---
4
-
5
- You are running `/hypo:query`. Full-text search across all wiki pages and projects, then synthesize an answer from the matching pages.
6
-
7
- ## What this does
8
-
9
- - Searches `pages/` and `projects/` for the given query terms
10
- - Returns matching files with a context excerpt and frontmatter summary
11
- - You then read the top results and synthesize an answer
12
-
13
- ---
14
-
15
- ## Step 1 — Locate package root
16
-
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 `/hypo:upgrade` on a plugin install) or reinstall instead of guessing the cache layout.
18
-
19
- 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`.
20
-
21
- ---
22
-
23
- ## Step 2 — Extract the query
24
-
25
- Use the search terms from the user's message. If no query was provided, ask:
26
-
27
- > "What would you like to search for in your wiki?"
28
-
29
- ---
30
-
31
- ## Step 3 — Run query
32
-
33
- ```bash
34
- node ${CLAUDE_PLUGIN_ROOT}/scripts/query.mjs \
35
- --q="<search terms>" \
36
- [--hypo-dir="<path>"] \
37
- [--limit=<n>] \
38
- [--json]
39
- ```
40
-
41
- Options:
42
- - `--q=<query>` — search query (required)
43
- - `--limit=<n>` — max results (default: 10)
44
- - `--json` — output results as JSON
45
-
46
- Show the output verbatim.
47
-
48
- ---
49
-
50
- ## Step 4 — Synthesize an answer
51
-
52
- Read the top matching pages (up to 5) and produce a synthesized response:
53
-
54
- 1. **Direct answer** — if the query has a clear answer from the wiki, state it first.
55
- 2. **Supporting pages** — list the relevant pages with one-line descriptions and `[[wikilink]]` references.
56
- 3. **Gaps** — if the wiki lacks coverage on the topic, note what is missing and suggest an ingest target.
57
-
58
- If zero results are returned, say so and offer to broaden the search or suggest using `/hypo:ingest` to add relevant sources.
59
-
60
- ---
61
-
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]].