hypomnema 1.8.4 → 1.9.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/CHANGELOG.md +36 -0
- package/README.ko.md +16 -15
- package/README.md +14 -13
- package/commands/crystallize.md +84 -30
- package/commands/doctor.md +3 -1
- package/docs/ARCHITECTURE.md +32 -9
- package/docs/CONTRIBUTING.md +5 -1
- package/hooks/atomic-write.mjs +44 -0
- package/hooks/base-store.mjs +409 -90
- package/hooks/close-gate-store.mjs +131 -16
- package/hooks/close-journal.mjs +141 -19
- package/hooks/close-receipt.mjs +559 -0
- package/hooks/hooks.json +2 -29
- package/hooks/hypo-auto-commit.mjs +80 -10
- package/hooks/hypo-auto-minimal-crystallize.mjs +129 -25
- package/hooks/hypo-auto-stage.mjs +13 -1
- package/hooks/hypo-close-guard.mjs +2 -1
- package/hooks/hypo-cwd-change.mjs +45 -20
- package/hooks/hypo-file-watch.mjs +44 -6
- package/hooks/hypo-first-prompt.mjs +15 -9
- package/hooks/hypo-hot-rebuild.mjs +169 -112
- package/hooks/hypo-personal-check.mjs +9 -1
- package/hooks/hypo-session-record.mjs +1 -1
- package/hooks/hypo-session-start.mjs +381 -18
- package/hooks/hypo-shared.mjs +2151 -185
- package/hooks/hypo-stop.mjs +207 -0
- package/hooks/proposal-store.mjs +166 -34
- package/hooks/shared.json +7 -1
- package/hooks/version-check.mjs +9 -7
- package/package.json +2 -1
- package/scripts/crystallize.mjs +3 -4
- package/scripts/doctor.mjs +506 -32
- package/scripts/init.mjs +32 -3
- package/scripts/lib/crystallize-close-apply.mjs +1949 -230
- package/scripts/lib/crystallize-close-check.mjs +43 -23
- package/scripts/lib/design-history-stale.mjs +36 -9
- package/scripts/lib/frontmatter.mjs +39 -0
- package/scripts/lib/hook-inventory.mjs +31 -1
- package/scripts/lib/page-usage.mjs +1 -1
- package/scripts/lib/pkg-provenance.mjs +7 -1
- package/scripts/lib/project-create.mjs +40 -44
- package/scripts/lib/schema-vocab.mjs +22 -11
- package/scripts/lib/template-schema-version.mjs +1 -0
- package/scripts/lib/time.mjs +46 -0
- package/scripts/lint.mjs +66 -18
- package/scripts/proposal.mjs +423 -18
- package/scripts/resume.mjs +3 -2
- package/scripts/session-audit.mjs +2 -1
- package/scripts/uninstall.mjs +15 -1
- package/scripts/upgrade.mjs +281 -90
- package/scripts/verify.mjs +2 -1
- package/scripts/weekly-report.mjs +2 -1
- package/templates/SCHEMA.md +27 -7
- package/templates/gitignore +11 -0
- package/templates/hypo-automation.md +10 -13
- package/templates/hypo-config.md +1 -1
- package/templates/hypo-guide.md +53 -25
- package/templates/hot.md +0 -24
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
* hot.md / session-state.md content is NOT re-injected here. On the SessionStart
|
|
12
12
|
* path the upstream hook already put it in the model's context, and this hook
|
|
13
13
|
* only forces the LLM to lead with a summary line drawn from it. On the
|
|
14
|
-
* cwd-change path
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* cwd-change path this hook does not rely on any context having reached the
|
|
15
|
+
* model. The hook reference does not document whether CwdChanged's
|
|
16
|
+
* systemMessage reaches the model; as of Claude Code 2.1.276 (checked
|
|
17
|
+
* 2026-09-18), tracing the installed binary shows it does not (see the
|
|
18
|
+
* transmission note in README.md), so that branch asks for a verbatim line
|
|
19
|
+
* instead of a summary.
|
|
17
20
|
* Marker expires after 10 minutes.
|
|
18
21
|
*/
|
|
19
22
|
|
|
@@ -74,12 +77,15 @@ process.stdin.on('end', () => {
|
|
|
74
77
|
// for the model to fill the placeholders with. Provide a concrete fallback
|
|
75
78
|
// line so the model doesn't leak literal `[one-line summary]` text on a
|
|
76
79
|
// first-ever session (codex v2 review 2026-05-26).
|
|
77
|
-
// A cwd-change marker arms this hook
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
80
|
+
// A cwd-change marker arms this hook. The hook reference does not
|
|
81
|
+
// document whether CwdChanged's systemMessage reaches the model; as of
|
|
82
|
+
// Claude Code 2.1.276 (checked 2026-09-18), tracing the installed binary
|
|
83
|
+
// shows it does not (see the transmission note in README.md), so this
|
|
84
|
+
// hook does not rely on [HOT] / [SESSION STATE] having reached the model
|
|
85
|
+
// for that move. Asking for a summary on context that never arrived
|
|
86
|
+
// would risk the model inventing one, or emitting the literal brackets.
|
|
87
|
+
// This is not a temporary branch: it holds until the documentation says
|
|
88
|
+
// otherwise.
|
|
83
89
|
const cwdMove = marker.source === 'cwd-change';
|
|
84
90
|
const exampleLine = cwdMove
|
|
85
91
|
? `${verb} ${projSafe}. What would you like to work on here?`
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* hypo-hot-rebuild.mjs
|
|
3
|
+
* hypo-hot-rebuild.mjs: Stop chain stage 1 (spawned by hypo-stop.mjs)
|
|
4
4
|
*
|
|
5
|
-
* Rebuilds root hot.md
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* This script manages: frontmatter, H2 structure, date fields.
|
|
5
|
+
* Rebuilds root hot.md as a projection on every session end: the row set and
|
|
6
|
+
* every field come from a fresh directory scan of `projects/*\/hot.md`
|
|
7
|
+
* (renderRootHotProjection in hypo-shared.mjs), never from parsing the
|
|
8
|
+
* previous root file. Claude no longer manages this file by hand; a project
|
|
9
|
+
* row exists exactly when that project has a hot.md, nothing more.
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
|
-
import {
|
|
12
|
+
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
14
13
|
import { join } from 'path';
|
|
15
14
|
import {
|
|
16
15
|
HYPO_DIR,
|
|
@@ -18,18 +17,33 @@ import {
|
|
|
18
17
|
formatGrowthMetrics,
|
|
19
18
|
deriveRootLogEntries,
|
|
20
19
|
recordTouchedPaths,
|
|
20
|
+
writeRootHotProjection,
|
|
21
|
+
writeRootHotHealthNotice,
|
|
22
|
+
rootHotBackupRecoveryNotice,
|
|
21
23
|
} from './hypo-shared.mjs';
|
|
22
|
-
import { advanceBase, hashContent } from './base-store.mjs';
|
|
23
24
|
|
|
24
|
-
const HOT_PATH = join(HYPO_DIR, 'hot.md');
|
|
25
25
|
const GROWTH_CACHE = join(HYPO_DIR, '.cache', 'last-session-growth.json');
|
|
26
26
|
|
|
27
|
-
// This
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
27
|
+
// This stage runs BEFORE hypo-auto-commit, and what guarantees that is
|
|
28
|
+
// hypo-stop.mjs: Stop carries ONE registration, and its STAGES list spawns the
|
|
29
|
+
// four stages in order, each one waited on before the next starts. The four
|
|
30
|
+
// used to be four separate Stop registrations, which Claude Code runs in
|
|
31
|
+
// parallel, so this sentence was false for as long as it stood: auto-commit
|
|
32
|
+
// could take the vault lock and commit between this file's write and the
|
|
33
|
+
// touched-paths claim at the bottom, leaving fresh hot.md/log.md bytes
|
|
34
|
+
// uncommitted with no failure anywhere to show for it.
|
|
35
|
+
//
|
|
36
|
+
// Put either of these four back into hooks.json as its own Stop registration
|
|
37
|
+
// and the guarantee is gone again, silently: this file still writes, still
|
|
38
|
+
// claims, and still reports success, and nothing about the order is checked at
|
|
39
|
+
// runtime. tests/session-hooks.test.mjs pins it from the outside instead, by
|
|
40
|
+
// asserting that this stage's hot.md reaches the commit auto-commit makes.
|
|
41
|
+
//
|
|
42
|
+
// This stage can write hot.md (rebuild) and log.md (deriveRootLogEntries), both
|
|
43
|
+
// hook-generated, not user Write/Edit, so hypo-auto-stage never sees them. Read
|
|
44
|
+
// session_id off stdin so whatever this stage writes still lands in the scoped
|
|
45
|
+
// commit's set; without this, a scope built from Write/Edit alone would
|
|
46
|
+
// silently drop these files from every session's auto-commit.
|
|
33
47
|
let sessionId = null;
|
|
34
48
|
try {
|
|
35
49
|
const raw = await new Promise((r) => {
|
|
@@ -43,102 +57,111 @@ try {
|
|
|
43
57
|
sessionId = null;
|
|
44
58
|
}
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function parsePointerRows(content) {
|
|
68
|
-
const rows = [];
|
|
69
|
-
for (const line of content.split('\n')) {
|
|
70
|
-
const m = line.match(/^\|\s*(.+?)\s*\|\s*.+?\s*\|\s*\[\[projects\/(.+?)\/hot\]\]\s*\|/);
|
|
71
|
-
if (m) rows.push({ name: m[1].trim(), slug: m[2].trim() });
|
|
72
|
-
}
|
|
73
|
-
return rows;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** @returns {boolean} true when hot.md was actually rewritten. */
|
|
60
|
+
/**
|
|
61
|
+
* Rebuild root hot.md as a directory-scan projection. No
|
|
62
|
+
* longer parses the previous root file's rows at all: the projection's row
|
|
63
|
+
* set is decided entirely by `writeRootHotProjection` from
|
|
64
|
+
* `projects/<slug>/hot.md` on disk, which is what lets a manually deleted row
|
|
65
|
+
* come back. See hypo-shared.mjs's projection section for the generation
|
|
66
|
+
* rules (blank date, not `|| today`; frontmatter `updated:` is the row-date
|
|
67
|
+
* max, not today) and for the scanError / backup semantics this function
|
|
68
|
+
* passes straight through.
|
|
69
|
+
*
|
|
70
|
+
* A scanError leaves a durable health notice for the next SessionStart to
|
|
71
|
+
* surface, since this hook's own stdout is suppressed at the bottom of this
|
|
72
|
+
* file and a Stop-time failure would otherwise be invisible until someone
|
|
73
|
+
* went looking at stderr.
|
|
74
|
+
* @param {string|null} sessionId
|
|
75
|
+
* @returns {{written: boolean, scanError: boolean, lockTimeout: boolean, backedUp: boolean, backupPath: string|null, gitignoreUpdated: boolean, warnings: string[], content: string|null}}
|
|
76
|
+
*/
|
|
77
77
|
function rebuild(sessionId) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
'[hypo-hot-rebuild] no session_id on stdin; base not advanced for hot.md\n',
|
|
78
|
+
// Mirrors hypo-session-start.mjs's own existsSync(HYPO_DIR) guard around its
|
|
79
|
+
// projection call. Without this, an install with no wiki at all (HYPO_DIR
|
|
80
|
+
// unresolved or absent) hits ENOENT on every Stop, forever, since this hook
|
|
81
|
+
// runs on every session end regardless of whether a wiki exists. The old
|
|
82
|
+
// rebuild() had the same guard in spirit (`if (!existsSync(HOT_PATH)) return
|
|
83
|
+
// false`), just against the file instead of the directory.
|
|
84
|
+
if (!existsSync(HYPO_DIR)) {
|
|
85
|
+
return {
|
|
86
|
+
written: false,
|
|
87
|
+
scanError: false,
|
|
88
|
+
lockTimeout: false,
|
|
89
|
+
backedUp: false,
|
|
90
|
+
backupPath: null,
|
|
91
|
+
gitignoreUpdated: false,
|
|
92
|
+
warnings: [],
|
|
93
|
+
content: null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// A read-error on the existing hot.md (BLOCKER fix) throws out of
|
|
97
|
+
// writeRootHotProjection instead of silently overwriting it; the outer
|
|
98
|
+
// try/catch around this whole rebuild() call already leaves a health
|
|
99
|
+
// notice for that case, same as any other unexpected error here.
|
|
100
|
+
// n1 fix: pass sessionId so this session's own digest lands in ITS OWN
|
|
101
|
+
// receipt, not just the shared global ownership record (see
|
|
102
|
+
// writeRootHotProjection's doc comment).
|
|
103
|
+
const result = writeRootHotProjection(HYPO_DIR, undefined, sessionId);
|
|
104
|
+
|
|
105
|
+
if (result.lockTimeout) {
|
|
106
|
+
// BLOCKER fix: the projection writer holds the vault lock now, so a Stop
|
|
107
|
+
// that overlaps another session's commit can find it taken. Nothing was
|
|
108
|
+
// written and nothing is claimed below (that is gated on
|
|
109
|
+
// `result.written`), so the only thing left to do is say so.
|
|
110
|
+
writeRootHotHealthNotice(
|
|
111
|
+
HYPO_DIR,
|
|
112
|
+
'이전 세션 종료 시 루트 hot.md 를 갱신하지 못했습니다: 다른 세션이 이 저장소를 쓰는 중이라 잠금을 얻지 못했습니다. 이전 내용을 그대로 두었고, 다음 세션 시작이나 종료 때 다시 시도합니다.',
|
|
113
|
+
);
|
|
114
|
+
} else if (result.scanError) {
|
|
115
|
+
writeRootHotHealthNotice(
|
|
116
|
+
HYPO_DIR,
|
|
117
|
+
`이전 세션 종료 시 루트 hot.md 갱신에 실패했습니다: projects/ 디렉터리를 읽을 수 없어 이전 파일을 그대로 두었습니다. 권한과 경로를 확인하고(\`ls -ld ${join(HYPO_DIR, 'projects')}\`), 고치면 다음 세션 시작이나 종료 때 자동으로 다시 시도합니다.`,
|
|
118
|
+
);
|
|
119
|
+
} else {
|
|
120
|
+
// scanError and a written result never coexist (writeRootHotProjection
|
|
121
|
+
// skips the write outright on a scanError), but a per-project warning and
|
|
122
|
+
// a migration backup CAN both be true on the same write, so both must
|
|
123
|
+
// land in the SAME notice call. writeRootHotHealthNotice holds exactly
|
|
124
|
+
// one pending message: a second call before the first is ever consumed
|
|
125
|
+
// silently replaces it (atomicWrite overwrites, it does not append), so
|
|
126
|
+
// issuing it twice here would drop whichever ran first instead of
|
|
127
|
+
// surfacing both.
|
|
128
|
+
const lines = [];
|
|
129
|
+
if (result.warnings.length > 0) {
|
|
130
|
+
// major-1/3: a per-project read failure kept its row (blank date), but
|
|
131
|
+
// Stop's own stdout is suppressed, so the reason still needs a durable
|
|
132
|
+
// notice or it never reaches anyone.
|
|
133
|
+
lines.push(
|
|
134
|
+
`이전 세션 종료 시 루트 hot.md 갱신 중 일부 프로젝트를 읽지 못했습니다:\n${result.warnings.join('\n')}\n` +
|
|
135
|
+
'해당 파일의 권한과 경로를 확인하면 다음 세션 시작이나 종료 때 자동으로 다시 읽습니다.',
|
|
137
136
|
);
|
|
138
137
|
}
|
|
139
|
-
|
|
138
|
+
if (result.backedUp && result.backupPath) {
|
|
139
|
+
// MAJOR fix (codex 3rd-tier finding 6): this used to name only the
|
|
140
|
+
// backup filename, with no recovery guidance at all. The reader had
|
|
141
|
+
// to already know NOT to copy it back onto root hot.md, and where the
|
|
142
|
+
// content actually goes. Stop's own stdout is suppressed, so a backup
|
|
143
|
+
// made here has no channel of its own besides this health notice,
|
|
144
|
+
// consumed at the next SessionStart (same delivery path already used
|
|
145
|
+
// for a scanError or a per-project warning above); before this fix
|
|
146
|
+
// that was the ONE surface where the guidance never arrived.
|
|
147
|
+
// rootHotBackupRecoveryNotice is the same sentence SessionStart's own
|
|
148
|
+
// write site uses, verbatim, so the two writers cannot drift apart.
|
|
149
|
+
lines.push(rootHotBackupRecoveryNotice(result.backupPath));
|
|
150
|
+
}
|
|
151
|
+
if (lines.length > 0) writeRootHotHealthNotice(HYPO_DIR, lines.join('\n\n'));
|
|
140
152
|
}
|
|
141
|
-
|
|
153
|
+
|
|
154
|
+
// This write used to advance the session's observed base for hot.md right
|
|
155
|
+
// here, because the write bypasses the Write/Edit tool and hypo-auto-stage's
|
|
156
|
+
// PostToolUse advanceBaseForWrite never sees it. That call is gone with root
|
|
157
|
+
// hot.md itself: the file is no longer one of base-store's
|
|
158
|
+
// `overwriteTargets`, so no session snapshots it, no close payload writes it,
|
|
159
|
+
// and there is no base for this regeneration to keep honest. Advancing one
|
|
160
|
+
// from here would MINT a guarded key that nothing ever compares, which is the
|
|
161
|
+
// exact shape that produced a `base-mismatch` park on a file no human had
|
|
162
|
+
// touched. If root hot.md ever needs a base again, it comes back through
|
|
163
|
+
// `overwriteTargets`, not through a hook's own write.
|
|
164
|
+
return result;
|
|
142
165
|
}
|
|
143
166
|
|
|
144
167
|
function emitGrowth() {
|
|
@@ -152,11 +175,24 @@ function emitGrowth() {
|
|
|
152
175
|
} catch {}
|
|
153
176
|
}
|
|
154
177
|
|
|
155
|
-
let
|
|
178
|
+
let hotResult = {
|
|
179
|
+
written: false,
|
|
180
|
+
scanError: false,
|
|
181
|
+
lockTimeout: false,
|
|
182
|
+
backedUp: false,
|
|
183
|
+
backupPath: null,
|
|
184
|
+
gitignoreUpdated: false,
|
|
185
|
+
warnings: [],
|
|
186
|
+
content: null,
|
|
187
|
+
};
|
|
156
188
|
try {
|
|
157
|
-
|
|
189
|
+
hotResult = rebuild(sessionId);
|
|
158
190
|
} catch (err) {
|
|
159
191
|
process.stderr.write(`[hypo-hot-rebuild] error: ${err?.message ?? String(err)}\n`);
|
|
192
|
+
writeRootHotHealthNotice(
|
|
193
|
+
HYPO_DIR,
|
|
194
|
+
`이전 세션 종료 시 루트 hot.md 갱신 중 오류가 발생했습니다: ${err?.message ?? String(err)}`,
|
|
195
|
+
);
|
|
160
196
|
}
|
|
161
197
|
// Auto-derive the root log.md session entry from each project's session-log
|
|
162
198
|
// heading (runs AFTER rebuild() so root hot.md is already fresh and isn't itself
|
|
@@ -177,9 +213,30 @@ try {
|
|
|
177
213
|
// set (see the sessionId comment above). No-op without a session_id.
|
|
178
214
|
try {
|
|
179
215
|
const touched = [];
|
|
180
|
-
if (
|
|
216
|
+
if (hotResult.written) touched.push('hot.md');
|
|
217
|
+
// The backup file itself is never claimed here: it is gitignored the
|
|
218
|
+
// moment writeRootHotProjection creates it (ensureVaultGitignorePattern),
|
|
219
|
+
// so nothing would ever stage or commit it anyway, and claiming a path git
|
|
220
|
+
// will never see as changed just leaves a permanently-dropped stale entry.
|
|
221
|
+
if (hotResult.gitignoreUpdated) touched.push('.gitignore');
|
|
181
222
|
if (logEntriesAdded > 0) touched.push('log.md');
|
|
182
|
-
if (touched.length > 0)
|
|
223
|
+
if (touched.length > 0) {
|
|
224
|
+
const claimed = recordTouchedPaths(HYPO_DIR, sessionId, touched);
|
|
225
|
+
// A failed claim here means this hook's own writes just landed on disk
|
|
226
|
+
// with nothing in this session's touched-paths set to bring them into
|
|
227
|
+
// the auto-commit that runs right after this hook, the same orphan
|
|
228
|
+
// hypo-session-start.mjs's own claim guards against, just on the Stop
|
|
229
|
+
// side. There is no earlier point to claim it from at Stop (unlike
|
|
230
|
+
// SessionStart, the touched paths are only known after rebuild() has
|
|
231
|
+
// already run), so this leaves a health notice instead of a pre-write
|
|
232
|
+
// guard.
|
|
233
|
+
if (!claimed && hotResult.written) {
|
|
234
|
+
writeRootHotHealthNotice(
|
|
235
|
+
HYPO_DIR,
|
|
236
|
+
`루트 hot.md 갱신 후 이번 세션의 커밋 범위 기록에 실패했습니다: ${touched.join(', ')} 가 미커밋 파일로 남을 수 있습니다.`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
183
240
|
} catch (err) {
|
|
184
241
|
process.stderr.write(`[hypo-hot-rebuild] touched-paths error: ${err?.message ?? String(err)}\n`);
|
|
185
242
|
}
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
isUnderProjectDirs,
|
|
36
36
|
resolveGateProjectOverride,
|
|
37
37
|
} from './hypo-shared.mjs';
|
|
38
|
+
import { closeCheckpointState, isCloseComplete } from './close-receipt.mjs';
|
|
38
39
|
|
|
39
40
|
const WARNING_FILE = join(homedir(), '.claude', 'state', 'wiki-context-warning.json');
|
|
40
41
|
|
|
@@ -118,9 +119,16 @@ process.stdin.on('end', () => {
|
|
|
118
119
|
|
|
119
120
|
let gate;
|
|
120
121
|
try {
|
|
122
|
+
// The marker only scopes this gate (log-only mode, attribution) when
|
|
123
|
+
// closeCheckpointState calls this session's close finished, the verdict
|
|
124
|
+
// Stop uses too. A marker whose receipt is missing or invalid is ignored.
|
|
125
|
+
const checkpoint = sessionId ? closeCheckpointState(HYPO_DIR, sessionId) : null;
|
|
121
126
|
gate = precompactGateStatus(HYPO_DIR, {
|
|
122
127
|
transcriptPath,
|
|
123
128
|
...(sessionId ? { sessionId } : {}),
|
|
129
|
+
...(checkpoint
|
|
130
|
+
? { closeMarker: isCloseComplete(checkpoint) ? checkpoint.marker : null }
|
|
131
|
+
: {}),
|
|
124
132
|
...(sessionCwd ? { sessionCwd } : {}),
|
|
125
133
|
...(attributionScope ? { attributionScope } : {}),
|
|
126
134
|
});
|
|
@@ -322,7 +330,7 @@ process.stdin.on('end', () => {
|
|
|
322
330
|
` [ ] 7. index.md — update Projects section if needed`,
|
|
323
331
|
` [ ] 8. log.md — append ## [${today}] session | <project-name>`,
|
|
324
332
|
` [ ] 9. hot.md — update projects/<name>/hot.md (no exceptions)`,
|
|
325
|
-
` [ ] 10. root hot.md
|
|
333
|
+
` [ ] 10. root hot.md: generated automatically from projects/*/hot.md; no manual edit needed`,
|
|
326
334
|
` [ ] 11. updated: field — verify today's date on all touched .md files`,
|
|
327
335
|
` [ ] 12. lint — run /hypo:lint; fix errors in files YOU touched`,
|
|
328
336
|
` (other projects' / shared-page debt is reported as non-blocking notice)`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* hypo-session-record.mjs
|
|
3
|
+
* hypo-session-record.mjs: Stop chain stage 2 (spawned by hypo-stop.mjs)
|
|
4
4
|
*
|
|
5
5
|
* Appends an entry to ~/hypomnema/.cache/sessions/index.jsonl for each
|
|
6
6
|
* completed session. The index.jsonl is the **primary** source for
|