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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +46 -0
- package/README.ko.md +8 -6
- package/README.md +8 -6
- package/commands/crystallize.md +32 -6
- package/commands/graph.md +7 -4
- package/commands/lint.md +8 -1
- package/commands/query.md +6 -4
- package/commands/resume.md +1 -0
- package/commands/verify.md +12 -1
- package/docs/ARCHITECTURE.md +26 -12
- package/docs/CONTRIBUTING.md +15 -6
- package/hooks/hypo-compact-guard.mjs +126 -23
- package/hooks/hypo-cwd-change.mjs +20 -19
- package/hooks/hypo-first-prompt.mjs +31 -16
- package/hooks/hypo-lookup.mjs +10 -5
- package/hooks/hypo-session-start.mjs +180 -2
- package/hooks/hypo-shared.mjs +410 -83
- package/hooks/hypo-web-fetch-ingest.mjs +9 -13
- package/package.json +2 -1
- package/scripts/doctor.mjs +32 -3
- package/scripts/graph.mjs +22 -2
- package/scripts/init.mjs +5 -1
- package/scripts/lib/crystallize-args.mjs +38 -2
- package/scripts/lib/crystallize-close-apply.mjs +745 -450
- package/scripts/lint.mjs +242 -39
- package/scripts/query.mjs +22 -2
- package/scripts/resume.mjs +177 -2
- package/scripts/upgrade.mjs +2 -2
- package/scripts/verify.mjs +22 -2
- package/templates/SCHEMA.md +23 -1
- package/templates/hypo-automation.md +4 -2
- package/templates/hypo-config.md +1 -1
- package/templates/hypo-guide.md +1 -1
- package/skills/crystallize/SKILL.md +0 -189
- package/skills/graph/SKILL.md +0 -58
- package/skills/ingest/SKILL.md +0 -107
- package/skills/lint/SKILL.md +0 -59
- package/skills/query/SKILL.md +0 -62
- package/skills/verify/SKILL.md +0 -96
package/scripts/verify.mjs
CHANGED
|
@@ -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='))
|
|
29
|
-
|
|
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;
|
package/templates/SCHEMA.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: Wiki Schema
|
|
3
3
|
type: schema
|
|
4
4
|
updated: YYYY-MM-DD
|
|
5
|
-
version: 2.
|
|
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` |
|
|
35
|
-
| `hypo-file-watch.mjs` | `FileChanged` |
|
|
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.
|
package/templates/hypo-config.md
CHANGED
package/templates/hypo-guide.md
CHANGED
|
@@ -164,7 +164,7 @@ Add to root `hot.md` active projects table.
|
|
|
164
164
|
|
|
165
165
|
#### Auto-project offer
|
|
166
166
|
|
|
167
|
-
When SessionStart
|
|
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]].
|
package/skills/graph/SKILL.md
DELETED
|
@@ -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]].
|
package/skills/ingest/SKILL.md
DELETED
|
@@ -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]].
|
package/skills/lint/SKILL.md
DELETED
|
@@ -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]].
|
package/skills/query/SKILL.md
DELETED
|
@@ -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]].
|