hypomnema 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/commands/audit.md +1 -1
- package/commands/crystallize.md +11 -9
- package/commands/doctor.md +1 -1
- package/commands/feedback.md +2 -2
- package/commands/graph.md +1 -1
- package/commands/ingest.md +1 -1
- package/commands/init.md +2 -2
- package/commands/lint.md +1 -1
- package/commands/query.md +1 -1
- package/commands/rename.md +1 -1
- package/commands/resume.md +1 -1
- package/commands/stats.md +1 -1
- package/commands/uninstall.md +4 -2
- package/commands/upgrade.md +1 -1
- package/commands/verify.md +1 -1
- package/docs/ARCHITECTURE.md +2 -2
- package/docs/CONTRIBUTING.md +20 -12
- package/hooks/hypo-auto-commit.mjs +2 -2
- package/hooks/hypo-auto-minimal-crystallize.mjs +30 -18
- package/hooks/hypo-compact-guard.mjs +2 -2
- package/hooks/hypo-cwd-change.mjs +27 -37
- package/hooks/hypo-personal-check.mjs +37 -22
- package/hooks/hypo-session-end.mjs +1 -1
- package/hooks/hypo-session-record.mjs +2 -2
- package/hooks/hypo-session-start.mjs +34 -40
- package/hooks/hypo-shared.mjs +364 -82
- package/hooks/version-check.mjs +1 -1
- package/package.json +5 -1
- package/scripts/check-tracker-ids.mjs +69 -31
- package/scripts/crystallize.mjs +322 -70
- package/scripts/doctor.mjs +7 -7
- package/scripts/feedback-sync.mjs +5 -5
- package/scripts/feedback.mjs +9 -10
- package/scripts/init.mjs +7 -7
- package/scripts/lib/check-tracker-ids.mjs +90 -32
- package/scripts/lib/design-history-stale.mjs +1 -1
- package/scripts/lib/extensions.mjs +7 -7
- package/scripts/lib/failure-type.mjs +1 -1
- package/scripts/lib/feedback-scope.mjs +1 -1
- package/scripts/lib/project-create.mjs +25 -7
- package/scripts/lib/schema-vocab.mjs +105 -1
- package/scripts/lib/template-schema-version.mjs +1 -1
- package/scripts/lib/wd-match.mjs +181 -0
- package/scripts/lib/wikilink.mjs +1 -1
- package/scripts/lint.mjs +14 -6
- package/scripts/rename.mjs +1 -1
- package/scripts/resume.mjs +20 -22
- package/scripts/session-audit.mjs +1 -1
- package/scripts/stats.mjs +3 -3
- package/scripts/uninstall.mjs +3 -3
- package/scripts/upgrade.mjs +17 -18
- package/skills/crystallize/SKILL.md +16 -8
- package/skills/graph/SKILL.md +2 -2
- package/skills/ingest/SKILL.md +4 -4
- package/skills/lint/SKILL.md +2 -2
- package/skills/query/SKILL.md +2 -2
- package/skills/verify/SKILL.md +2 -2
- package/templates/SCHEMA.md +12 -4
- package/templates/hypo-config.md +1 -1
- package/templates/hypo-guide.md +24 -15
- package/templates/projects/_template/hot.md +1 -1
- package/scripts/fix-status-verify.mjs +0 -256
- package/scripts/lib/adr-corpus.mjs +0 -79
- package/scripts/lib/fix-manifest.mjs +0 -109
- 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 `
|
|
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
|
-
|
|
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
|
|
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** (
|
|
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.
|
|
@@ -61,21 +61,29 @@ When uncertain, surface the question rather than skip it. None of the four block
|
|
|
61
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
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
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
|
-
|
|
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.)
|
|
65
66
|
|
|
66
67
|
After completing the checklist, verify it before reporting:
|
|
67
68
|
|
|
68
69
|
```bash
|
|
69
|
-
node
|
|
70
|
+
node ${CLAUDE_PLUGIN_ROOT}/scripts/crystallize.mjs --check-session-close [--hypo-dir="<path>"]
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
This runs the **full** PreCompact gate via the shared `precompactGateStatus
|
|
73
|
-
|
|
73
|
+
This runs the **full** PreCompact gate via the shared `precompactGateStatus`:
|
|
74
|
+
close files (`missing` / `stale`) plus lint blockers, stale
|
|
74
75
|
design-history, and feedback projection over-cap/conflict. Fix every `✗` it
|
|
75
76
|
reports and re-run until it prints **"Compact-ready"** — that is the signal the
|
|
76
77
|
session is closed. A close-files-only pass is not enough; the real `/compact`
|
|
77
78
|
also blocks on those other checks.
|
|
78
79
|
|
|
80
|
+
Optional `--project=<slug>` narrows the check to ONE project (close status plus
|
|
81
|
+
lint scope). It is a project-scoped **diagnostic**, not the compact-ready
|
|
82
|
+
signal. A green `--project` result (JSON `scope: "project"`) means only that
|
|
83
|
+
project is close-complete; another today-active project can still block
|
|
84
|
+
`/compact`. Use the plain `--check-session-close` (no `--project`) for the
|
|
85
|
+
go/no-go close signal.
|
|
86
|
+
|
|
79
87
|
**When using `--apply-session-close --session-id=<id>`** (the payload-driven
|
|
80
88
|
path), the `--session-id` must be the main conversation's session id. Do NOT
|
|
81
89
|
extract it from a background task or Agent output path (e.g., a UUID from
|
package/skills/graph/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
40
|
+
node ${CLAUDE_PLUGIN_ROOT}/scripts/graph.mjs \
|
|
41
41
|
[--wiki-dir="<path>"] \
|
|
42
42
|
[--format=json|mermaid|dot] \
|
|
43
43
|
[--min-edges=<n>]
|
package/skills/ingest/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
package/skills/lint/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
28
|
+
node ${CLAUDE_PLUGIN_ROOT}/scripts/lint.mjs [--wiki-dir="<path>"] [--json] [--fix]
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
Options:
|
package/skills/query/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
34
|
+
node ${CLAUDE_PLUGIN_ROOT}/scripts/query.mjs \
|
|
35
35
|
--q="<search terms>" \
|
|
36
36
|
[--wiki-dir="<path>"] \
|
|
37
37
|
[--limit=<n>] \
|
package/skills/verify/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
26
|
+
node ${CLAUDE_PLUGIN_ROOT}/scripts/verify.mjs \
|
|
27
27
|
[--wiki-dir="<path>"] \
|
|
28
28
|
[--file="<path>"] \
|
|
29
29
|
[--json]
|
package/templates/SCHEMA.md
CHANGED
|
@@ -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
|
|
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`
|
|
@@ -154,9 +154,11 @@ Edit the feedback page only — never hand-edit the generated
|
|
|
154
154
|
|
|
155
155
|
## 4. Tag Vocabulary
|
|
156
156
|
|
|
157
|
-
Use lowercase, hyphenated tags.
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
Use lowercase, hyphenated tags. `lint` hard-blocks forbidden patterns (PascalCase,
|
|
158
|
+
plurals, whitespace, generic words); an unknown but well-formed tag is a warning,
|
|
159
|
+
and a session close auto-registers it into the `### Pending` section below so the
|
|
160
|
+
next lint accepts it. Promote a pending tag into a category here (and in
|
|
161
|
+
`~/hypomnema/SCHEMA.md`) once it has settled.
|
|
160
162
|
|
|
161
163
|
**Meta**: `wiki`, `index`, `pages`, `home`, `overview`, `guide`, `operations`, `schema`, `reference`, `hypo`, `commands`, `hot-cache`, `migration`
|
|
162
164
|
**Workflow**: `automation`, `hooks`, `observability`, `autonomy`, `wiki-health`, `weekly`
|
|
@@ -165,6 +167,12 @@ Extend this list (and `~/hypomnema/SCHEMA.md`) before introducing a new tag.
|
|
|
165
167
|
**Status**: `active`, `completed`, `archived`, `draft`, `stable`, `deprecated`, `needs-review`, `proposed`, `superseded`
|
|
166
168
|
**Content classification**: `learning`, `tip`, `feedback`, `gotcha`, `concept`, `pattern`
|
|
167
169
|
|
|
170
|
+
### Pending (auto-registered)
|
|
171
|
+
|
|
172
|
+
Tags auto-registered by a crystallize close because they were not yet in the
|
|
173
|
+
vocabulary above. Review periodically: promote each into a category and delete it
|
|
174
|
+
here, or drop the tag from the page.
|
|
175
|
+
|
|
168
176
|
### Forbidden patterns
|
|
169
177
|
|
|
170
178
|
| Pattern | Reason | Use instead |
|
package/templates/hypo-config.md
CHANGED
package/templates/hypo-guide.md
CHANGED
|
@@ -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** (
|
|
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,27 +82,30 @@ 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
|
|
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 `
|
|
88
|
-
|
|
89
|
-
6. Verify with `
|
|
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
|
|
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
|
|
95
95
|
guarantee: the live gate can still differ on a context-≥70% prompt,
|
|
96
96
|
`HYPO_SKIP_GATE`, or a transcript-scoped lint error — pass `--transcript-path`
|
|
97
97
|
to include the last.) Pass `--session-id=<id>` to also see `marker_present`
|
|
98
|
-
(step 7).
|
|
99
|
-
|
|
98
|
+
(step 7). `--project=<slug>` narrows the check to one project (a scoped
|
|
99
|
+
diagnostic, JSON `scope: "project"`): green there means only that slug is
|
|
100
|
+
close-complete, **not** that `/compact` is globally unblocked. Use the plain
|
|
101
|
+
check for the go/no-go signal.
|
|
102
|
+
7. Record the session-closed marker. The Stop hook blocks until this
|
|
100
103
|
session's per-session marker exists, and a hand-edit close (writing the files
|
|
101
104
|
directly + committing) never writes it; the marker is written only by the
|
|
102
|
-
crystallize writer, never by the hook (
|
|
103
|
-
close via `
|
|
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>`),
|
|
104
107
|
which writes the marker once the gate is green. Hand-edit recovery: after
|
|
105
|
-
committing the files, run `
|
|
108
|
+
committing the files, run `/hypo:crystallize` (`--mark-session-closed --session-id=<id> --transcript-path=<path>`).
|
|
106
109
|
Both writers gate the marker on the SAME `precompactGateStatus` as `/compact`,
|
|
107
110
|
so the marker only lands when step 6 would print **"Compact-ready"**.
|
|
108
111
|
|
|
@@ -150,7 +153,7 @@ projects/<name>/
|
|
|
150
153
|
|
|
151
154
|
Add to root `hot.md` active projects table.
|
|
152
155
|
|
|
153
|
-
#### Auto-project offer
|
|
156
|
+
#### Auto-project offer
|
|
154
157
|
|
|
155
158
|
When SessionStart / CwdChanged injects a line like
|
|
156
159
|
`[WIKI: cwd '<name>'에 매칭되는 프로젝트가 없습니다. 자동 생성할까요? (Y/n)]`,
|
|
@@ -163,8 +166,14 @@ ignore it:**
|
|
|
163
166
|
2. **On Yes** — run the scaffold helper once (it substitutes tokens, creates
|
|
164
167
|
the project files, adds the root `hot.md` row, and logs the entry):
|
|
165
168
|
```
|
|
166
|
-
node
|
|
169
|
+
node ${CLAUDE_PLUGIN_ROOT}/scripts/lib/project-create.mjs --name <slug> --working-dir "$(pwd)"
|
|
167
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.
|
|
168
177
|
Then tell the user: "Created project `<name>` at
|
|
169
178
|
`~/hypomnema/projects/<name>/`. Edit `index.md` to refine." Do **not** hand-write
|
|
170
179
|
the five files — the helper keeps substitution and registration consistent.
|
|
@@ -226,8 +235,8 @@ Hypomnema v1.1.0 ships an **autonomy score** so you can see whether the wiki is
|
|
|
226
235
|
- `staleness-skip` — session older than the audit window (default 30d)
|
|
227
236
|
- **Where it lives:**
|
|
228
237
|
- Per-session transcript index: `<hypo-root>/.cache/sessions/index.jsonl` (written by the Stop hook `hypo-session-record.mjs`).
|
|
229
|
-
- Fallback source: `~/.claude/projects/<encoded>/*.jsonl` (used when the index is empty
|
|
230
|
-
- Reports: `journal/weekly/<YYYY-Www>.md` (spec §6.4 SoT), generated by `
|
|
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.
|
|
231
240
|
- **Definitions:** the 0% / 100% endpoints, the formal score sketch, and open questions live in `pages/observability/_index.md`.
|
|
232
241
|
|
|
233
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/
|
|
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
|
-
});
|