hypomnema 1.0.1 → 1.2.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 +12 -5
- package/README.md +12 -5
- package/commands/audit.md +46 -0
- package/commands/crystallize.md +113 -23
- package/commands/feedback.md +40 -26
- package/commands/ingest.md +31 -9
- package/commands/upgrade.md +2 -2
- package/docs/ARCHITECTURE.md +83 -9
- package/docs/CONTRIBUTING.md +2 -2
- package/hooks/hooks.json +39 -1
- package/hooks/hypo-auto-commit.mjs +23 -4
- package/hooks/hypo-auto-minimal-crystallize.mjs +145 -0
- package/hooks/hypo-auto-stage.mjs +9 -5
- package/hooks/hypo-compact-guard.mjs +33 -24
- package/hooks/hypo-cwd-change.mjs +107 -24
- package/hooks/hypo-file-watch.mjs +23 -10
- package/hooks/hypo-first-prompt.mjs +37 -23
- package/hooks/hypo-hot-rebuild.mjs +31 -8
- package/hooks/hypo-lookup.mjs +171 -65
- package/hooks/hypo-personal-check.mjs +207 -112
- package/hooks/hypo-pre-commit.mjs +46 -0
- package/hooks/hypo-session-end.mjs +58 -0
- package/hooks/hypo-session-record.mjs +60 -0
- package/hooks/hypo-session-start.mjs +312 -44
- package/hooks/hypo-shared.mjs +880 -28
- package/hooks/hypo-web-fetch-ingest.mjs +121 -0
- package/hooks/version-check-fetch.mjs +74 -0
- package/hooks/version-check.mjs +184 -0
- package/package.json +17 -3
- package/scripts/crystallize.mjs +623 -18
- package/scripts/doctor.mjs +739 -46
- package/scripts/feedback-sync.mjs +974 -0
- package/scripts/feedback.mjs +253 -44
- package/scripts/graph.mjs +35 -22
- package/scripts/ingest.mjs +89 -16
- package/scripts/init.mjs +442 -114
- package/scripts/lib/design-history-stale.mjs +83 -0
- package/scripts/lib/extensions.mjs +749 -0
- package/scripts/lib/frontmatter.mjs +5 -1
- package/scripts/lib/hypo-ignore.mjs +12 -10
- package/scripts/lib/pkg-json.mjs +23 -5
- package/scripts/lib/project-create.mjs +225 -0
- package/scripts/lib/schema-vocab.mjs +96 -0
- package/scripts/lint.mjs +238 -31
- package/scripts/query.mjs +26 -10
- package/scripts/resume.mjs +11 -5
- package/scripts/session-audit.mjs +277 -0
- package/scripts/smoke-pack.mjs +224 -0
- package/scripts/stats.mjs +24 -10
- package/scripts/uninstall.mjs +369 -48
- package/scripts/upgrade.mjs +766 -195
- package/scripts/verify.mjs +24 -14
- package/scripts/weekly-report.mjs +211 -0
- package/skills/crystallize/SKILL.md +24 -7
- package/skills/graph/SKILL.md +4 -0
- package/skills/ingest/SKILL.md +29 -5
- package/skills/lint/SKILL.md +4 -0
- package/skills/query/SKILL.md +4 -0
- package/skills/verify/SKILL.md +4 -0
- package/templates/.hypoignore +19 -2
- package/templates/Home.md +2 -0
- package/templates/SCHEMA.md +61 -6
- package/templates/extensions/agents/.gitkeep +0 -0
- package/templates/extensions/commands/.gitkeep +0 -0
- package/templates/extensions/hooks/.gitkeep +0 -0
- package/templates/extensions/skills/.gitkeep +0 -0
- package/templates/gitignore +5 -0
- package/templates/hot.md +2 -0
- package/templates/hypo-config.md +1 -1
- package/templates/hypo-guide.md +63 -1
- package/templates/hypo-help.md +1 -1
- package/templates/pages/observability/_index.md +77 -0
- package/templates/projects/_template/index.md +2 -2
- package/templates/projects/_template/prd.md +1 -1
package/scripts/verify.mjs
CHANGED
|
@@ -26,8 +26,8 @@ function parseArgs(argv) {
|
|
|
26
26
|
const args = { hypoDir: null, file: null, json: false };
|
|
27
27
|
for (const arg of argv.slice(2)) {
|
|
28
28
|
if (arg.startsWith('--hypo-dir=')) args.hypoDir = expandHome(arg.slice(11));
|
|
29
|
-
else if (arg.startsWith('--file='))
|
|
30
|
-
else if (arg === '--json')
|
|
29
|
+
else if (arg.startsWith('--file=')) args.file = expandHome(arg.slice(7));
|
|
30
|
+
else if (arg === '--json') args.json = true;
|
|
31
31
|
}
|
|
32
32
|
if (!args.hypoDir) args.hypoDir = resolveHypoRoot();
|
|
33
33
|
return args;
|
|
@@ -55,7 +55,11 @@ function parseFrontmatter(content) {
|
|
|
55
55
|
for (const line of m[1].split('\n')) {
|
|
56
56
|
const idx = line.indexOf(':');
|
|
57
57
|
if (idx < 0) continue;
|
|
58
|
-
fm[line.slice(0, idx).trim()] = line
|
|
58
|
+
fm[line.slice(0, idx).trim()] = line
|
|
59
|
+
.slice(idx + 1)
|
|
60
|
+
.trim()
|
|
61
|
+
.replace(/\s*#.*$/, '')
|
|
62
|
+
.replace(/^["']|["']$/g, '');
|
|
59
63
|
}
|
|
60
64
|
return fm;
|
|
61
65
|
}
|
|
@@ -72,18 +76,22 @@ if (args.file) {
|
|
|
72
76
|
files = [args.file];
|
|
73
77
|
} else {
|
|
74
78
|
const ignorePatterns = loadHypoIgnore(args.hypoDir);
|
|
75
|
-
const scanDirs = ['pages', 'projects'].map(d => join(args.hypoDir, d));
|
|
76
|
-
files = scanDirs.flatMap(d => collectMdFiles(d, [], args.hypoDir, ignorePatterns));
|
|
79
|
+
const scanDirs = ['pages', 'projects'].map((d) => join(args.hypoDir, d));
|
|
80
|
+
files = scanDirs.flatMap((d) => collectMdFiles(d, [], args.hypoDir, ignorePatterns));
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
const overdue
|
|
83
|
+
const overdue = [];
|
|
80
84
|
const upcoming = [];
|
|
81
|
-
const missing
|
|
82
|
-
const ok
|
|
85
|
+
const missing = [];
|
|
86
|
+
const ok = [];
|
|
83
87
|
|
|
84
88
|
for (const file of files) {
|
|
85
89
|
let content;
|
|
86
|
-
try {
|
|
90
|
+
try {
|
|
91
|
+
content = readFileSync(file, 'utf-8');
|
|
92
|
+
} catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
87
95
|
const fm = parseFrontmatter(content);
|
|
88
96
|
if (!fm) continue;
|
|
89
97
|
|
|
@@ -152,16 +160,18 @@ if (ok.length > 0 && overdue.length === 0 && upcoming.length === 0) {
|
|
|
152
160
|
}
|
|
153
161
|
|
|
154
162
|
const summary = [
|
|
155
|
-
overdue.length
|
|
163
|
+
overdue.length ? `${overdue.length} overdue` : '',
|
|
156
164
|
upcoming.length ? `${upcoming.length} due soon` : '',
|
|
157
|
-
missing.length
|
|
158
|
-
ok.length
|
|
159
|
-
]
|
|
165
|
+
missing.length ? `${missing.length} missing verify_by` : '',
|
|
166
|
+
ok.length ? `${ok.length} ok` : '',
|
|
167
|
+
]
|
|
168
|
+
.filter(Boolean)
|
|
169
|
+
.join(', ');
|
|
160
170
|
|
|
161
171
|
console.log(`Result: ${summary || 'nothing to verify'}`);
|
|
162
172
|
|
|
163
173
|
// Emit signal lines for overdue/upcoming pages so /hypo:verify skill can drive review
|
|
164
|
-
const needsReview = [...overdue, ...upcoming].filter(p => p.verify_by);
|
|
174
|
+
const needsReview = [...overdue, ...upcoming].filter((p) => p.verify_by);
|
|
165
175
|
if (needsReview.length > 0) {
|
|
166
176
|
console.log('');
|
|
167
177
|
for (const p of needsReview) {
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* weekly-report.mjs — Weekly observability report (Lane E)
|
|
4
|
+
*
|
|
5
|
+
* Aggregates session-audit results into a Markdown report under
|
|
6
|
+
* `<hypo-dir>/journal/weekly/<YYYY-Www>.md` (spec §6.4 SoT).
|
|
7
|
+
*
|
|
8
|
+
* The autonomy score is heuristic on purpose — see
|
|
9
|
+
* pages/observability/_index.md for the definition and the 4-week
|
|
10
|
+
* baseline plan before we revisit LLM-judge classification.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node scripts/weekly-report.mjs [--hypo-dir=<path>] [--week=YYYY-Www]
|
|
14
|
+
* [--limit N] [--write] [--json]
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
18
|
+
import { join } from 'path';
|
|
19
|
+
import { resolveHypoRoot, expandHome } from './lib/hypo-root.mjs';
|
|
20
|
+
import { loadSessionEntries, auditEntries } from './session-audit.mjs';
|
|
21
|
+
|
|
22
|
+
function parseArgs(argv) {
|
|
23
|
+
const args = {
|
|
24
|
+
hypoDir: null,
|
|
25
|
+
week: null,
|
|
26
|
+
limit: 200,
|
|
27
|
+
write: false,
|
|
28
|
+
json: false,
|
|
29
|
+
fallbackAll: false,
|
|
30
|
+
};
|
|
31
|
+
for (const arg of argv.slice(2)) {
|
|
32
|
+
if (arg.startsWith('--hypo-dir=')) args.hypoDir = expandHome(arg.slice(11));
|
|
33
|
+
else if (arg.startsWith('--week=')) args.week = arg.slice(7);
|
|
34
|
+
else if (arg.startsWith('--limit=')) args.limit = parseInt(arg.slice(8), 10) || 200;
|
|
35
|
+
else if (arg === '--write') args.write = true;
|
|
36
|
+
else if (arg === '--json') args.json = true;
|
|
37
|
+
else if (arg === '--fallback-all-projects') args.fallbackAll = true;
|
|
38
|
+
}
|
|
39
|
+
if (!args.hypoDir) args.hypoDir = resolveHypoRoot();
|
|
40
|
+
return args;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isoWeek(date) {
|
|
44
|
+
// ISO-8601 week: Thursday in current week decides the year.
|
|
45
|
+
const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
46
|
+
const dayNum = d.getUTCDay() || 7;
|
|
47
|
+
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
|
48
|
+
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
49
|
+
const week = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
|
|
50
|
+
return `${d.getUTCFullYear()}-W${String(week).padStart(2, '0')}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseWeekArg(spec) {
|
|
54
|
+
// Accepts YYYY-Www (ISO 8601). Returns the Monday of that ISO week, or
|
|
55
|
+
// null if the week number does not exist in that year (round-trips via
|
|
56
|
+
// isoWeek to reject W00, W54, and W53 in non-53-week years).
|
|
57
|
+
const m = String(spec).match(/^(\d{4})-W(\d{2})$/);
|
|
58
|
+
if (!m) return null;
|
|
59
|
+
const year = parseInt(m[1], 10);
|
|
60
|
+
const week = parseInt(m[2], 10);
|
|
61
|
+
if (week < 1 || week > 53) return null;
|
|
62
|
+
const jan4 = new Date(Date.UTC(year, 0, 4));
|
|
63
|
+
const jan4Dow = jan4.getUTCDay() || 7;
|
|
64
|
+
const week1Mon = new Date(jan4);
|
|
65
|
+
week1Mon.setUTCDate(jan4.getUTCDate() - (jan4Dow - 1));
|
|
66
|
+
const weekStart = new Date(week1Mon);
|
|
67
|
+
weekStart.setUTCDate(week1Mon.getUTCDate() + (week - 1) * 7);
|
|
68
|
+
if (isoWeek(weekStart) !== spec) return null;
|
|
69
|
+
return weekStart;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function filterToWeek(results, weekLabel) {
|
|
73
|
+
return results.filter((r) => {
|
|
74
|
+
if (!r.recorded_at) return false;
|
|
75
|
+
return isoWeek(new Date(r.recorded_at)) === weekLabel;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function tally(results) {
|
|
80
|
+
const out = {
|
|
81
|
+
normal: 0,
|
|
82
|
+
'search-0': 0,
|
|
83
|
+
'search-many': 0,
|
|
84
|
+
'ingest-missed': 0,
|
|
85
|
+
'staleness-skip': 0,
|
|
86
|
+
};
|
|
87
|
+
for (const r of results) out[r.classification] = (out[r.classification] || 0) + 1;
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function autonomyScore(results) {
|
|
92
|
+
// Heuristic v0 (see pages/observability/_index.md §3 for the formal sketch).
|
|
93
|
+
// Numerator = weighted "wiki was actually used" signals.
|
|
94
|
+
// Denominator = same signals expected (1 per non-stale session, plus URL→ingest opportunities).
|
|
95
|
+
let num = 0;
|
|
96
|
+
let den = 0;
|
|
97
|
+
for (const r of results) {
|
|
98
|
+
if (r.classification === 'staleness-skip') continue;
|
|
99
|
+
const m = r.metrics;
|
|
100
|
+
den += 1; // at minimum, expect *some* wiki engagement per real session
|
|
101
|
+
num += Math.min(m.search_count, 3) + m.ingest_count * 3 + m.feedback_count * 2;
|
|
102
|
+
if (m.urls > 0) {
|
|
103
|
+
// Each URL is a missed-ingest opportunity (weight 2). Numerator is *not*
|
|
104
|
+
// boosted here — `ingest_count` is already credited above; what URLs do
|
|
105
|
+
// is raise the bar for what "fully autonomous" looks like.
|
|
106
|
+
den += Math.min(m.urls, 5) * 2;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (den === 0) return 0;
|
|
110
|
+
return Math.min(100, Math.max(0, Math.round((num / den) * 100)));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function buildReport(
|
|
114
|
+
hypoDir,
|
|
115
|
+
{ week, limit = 200, now = Date.now(), fallbackAll = false } = {},
|
|
116
|
+
) {
|
|
117
|
+
const entries = loadSessionEntries(hypoDir, { fallbackAll });
|
|
118
|
+
const audited = auditEntries(entries, { limit, maxAgeDays: 365, now });
|
|
119
|
+
const weekLabel = week || isoWeek(new Date(now));
|
|
120
|
+
const weekResults = filterToWeek(audited, weekLabel);
|
|
121
|
+
const counts = tally(weekResults);
|
|
122
|
+
const score = autonomyScore(weekResults);
|
|
123
|
+
return { week: weekLabel, count: weekResults.length, counts, score, results: weekResults };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function renderMarkdown(report) {
|
|
127
|
+
const { week, count, counts, score, results } = report;
|
|
128
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
129
|
+
const lines = [];
|
|
130
|
+
lines.push('---');
|
|
131
|
+
lines.push(`title: Observability — Week ${week}`);
|
|
132
|
+
lines.push('tags: [observability, autonomy, weekly]');
|
|
133
|
+
lines.push('type: weekly-journal');
|
|
134
|
+
lines.push(`week: ${week}`);
|
|
135
|
+
lines.push(`generated_by: manual`);
|
|
136
|
+
lines.push(`updated: ${today}`);
|
|
137
|
+
lines.push('---');
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push(`# Observability — Week ${week}`);
|
|
140
|
+
lines.push('');
|
|
141
|
+
lines.push(
|
|
142
|
+
`> Generated by \`scripts/weekly-report.mjs\`. Heuristic definition: [[pages/observability/_index]].`,
|
|
143
|
+
);
|
|
144
|
+
lines.push('');
|
|
145
|
+
lines.push(`## Autonomy score (heuristic v0): **${score}%**`);
|
|
146
|
+
lines.push('');
|
|
147
|
+
lines.push(`Sessions audited: ${count}`);
|
|
148
|
+
lines.push('');
|
|
149
|
+
lines.push('| Classification | Count |');
|
|
150
|
+
lines.push('|---|---|');
|
|
151
|
+
for (const k of ['normal', 'search-0', 'search-many', 'ingest-missed', 'staleness-skip']) {
|
|
152
|
+
lines.push(`| ${k} | ${counts[k] || 0} |`);
|
|
153
|
+
}
|
|
154
|
+
lines.push('');
|
|
155
|
+
if (results.length === 0) {
|
|
156
|
+
lines.push(
|
|
157
|
+
'_No sessions recorded in this week. If this is unexpected, check `hooks/hypo-session-record.mjs` and `.cache/sessions/index.jsonl`._',
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
lines.push('## Sessions');
|
|
161
|
+
lines.push('');
|
|
162
|
+
lines.push('| Session | Class | search | ingest | urls | recorded |');
|
|
163
|
+
lines.push('|---|---|---|---|---|---|');
|
|
164
|
+
for (const r of results) {
|
|
165
|
+
const m = r.metrics;
|
|
166
|
+
lines.push(
|
|
167
|
+
`| ${r.session_id} | ${r.classification} | ${m.search_count} | ${m.ingest_count} | ${m.urls} | ${r.recorded_at || '—'} |`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return lines.join('\n') + '\n';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function isMain() {
|
|
175
|
+
try {
|
|
176
|
+
return import.meta.url === `file://${process.argv[1]}`;
|
|
177
|
+
} catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (isMain()) {
|
|
183
|
+
const args = parseArgs(process.argv);
|
|
184
|
+
if (args.week && !parseWeekArg(args.week)) {
|
|
185
|
+
console.error(`Error: invalid --week value "${args.week}" (expected YYYY-Www)`);
|
|
186
|
+
process.exit(2);
|
|
187
|
+
}
|
|
188
|
+
const report = buildReport(args.hypoDir, {
|
|
189
|
+
week: args.week,
|
|
190
|
+
limit: args.limit,
|
|
191
|
+
fallbackAll: args.fallbackAll,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (args.json) {
|
|
195
|
+
console.log(JSON.stringify(report, null, 2));
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const md = renderMarkdown(report);
|
|
200
|
+
|
|
201
|
+
if (args.write) {
|
|
202
|
+
const dir = join(args.hypoDir, 'journal', 'weekly');
|
|
203
|
+
mkdirSync(dir, { recursive: true });
|
|
204
|
+
const path = join(dir, `${report.week}.md`);
|
|
205
|
+
writeFileSync(path, md);
|
|
206
|
+
console.log(`✓ Wrote ${path}`);
|
|
207
|
+
console.log(` Autonomy score: ${report.score}% (sessions: ${report.count})`);
|
|
208
|
+
} else {
|
|
209
|
+
process.stdout.write(md);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Close a session (steps 1~6) and, on request, consolidate scattered wiki knowledge into stable pages (steps 7~11)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
You are running `/hypo:crystallize`.
|
|
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)** — gate the 5 mandatory memory files plus open-questions (conditional) so `/compact` can pass.
|
|
8
|
+
2. **Synthesis (steps 7~11)** — surface tag clusters, orphan pages, and drafts that are ready to consolidate.
|
|
9
|
+
|
|
10
|
+
When invoked at the end of a session (or with phrases like "세션 종료", "wrap up"), run the session-close checklist first. The synthesis scan only runs after close is confirmed and the user agrees.
|
|
6
11
|
|
|
7
12
|
## What this does
|
|
8
13
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Lists draft / stub pages that could be fleshed out
|
|
12
|
-
- After the script runs, you help the user pick what to crystallize and do it
|
|
14
|
+
- **Close mode**: walks the 6-step checklist (session-state, project hot.md, root hot.md, session-log, open-questions(변경 시), log.md) and verifies via `crystallize.mjs --check-session-close` — same gate the PreCompact hook runs.
|
|
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.
|
|
13
16
|
|
|
14
17
|
---
|
|
15
18
|
|
|
@@ -49,7 +52,17 @@ If `/hypo:crystallize` was invoked as a session-close action, run through this c
|
|
|
49
52
|
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.
|
|
50
53
|
6. **log.md** — append a `session` entry to `<wiki-root>/log.md`.
|
|
51
54
|
|
|
52
|
-
After completing the checklist,
|
|
55
|
+
After completing the checklist, verify it before reporting:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node <package-root>/scripts/crystallize.mjs --check-session-close [--hypo-dir="<path>"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This runs the same strict check as the PreCompact hard gate (fix #17). Fix any
|
|
62
|
+
file reported `missing` or `stale` and re-run until it passes — otherwise
|
|
63
|
+
`/compact` will be blocked.
|
|
64
|
+
|
|
65
|
+
Once it passes, report each item with ✓ and ask: "Session closed. Would you like to also run knowledge synthesis now, or stop here?"
|
|
53
66
|
|
|
54
67
|
---
|
|
55
68
|
|
|
@@ -83,3 +96,7 @@ evidence_strength: inferred
|
|
|
83
96
|
|
|
84
97
|
3. Cross-link all source pages back to the synthesis with `[[wikilink]]`.
|
|
85
98
|
4. Add the synthesis to `index.md` under `## Pages — Syntheses`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
> **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score — see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
|
package/skills/graph/SKILL.md
CHANGED
|
@@ -52,3 +52,7 @@ node <package-root>/scripts/graph.mjs \
|
|
|
52
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
53
|
|
|
54
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]]`. The observability audit counts citations toward the autonomy score — see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
|
package/skills/ingest/SKILL.md
CHANGED
|
@@ -16,14 +16,14 @@ You are running `/hypo:ingest`. Add a new source document to `sources/` and crea
|
|
|
16
16
|
|
|
17
17
|
Locate the Hypomnema package root (the directory two levels above this file (`skills/<name>/SKILL.md` → package root)).
|
|
18
18
|
|
|
19
|
-
If the user specified a wiki directory, pass it as `--
|
|
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
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
## Step 2 — Run ingest status check
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
node <package-root>/scripts/ingest.mjs [--
|
|
26
|
+
node <package-root>/scripts/ingest.mjs [--hypo-dir="<path>"] [--json]
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Options:
|
|
@@ -33,7 +33,27 @@ Show the output verbatim.
|
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
## Step 3 —
|
|
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 <package-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 <package-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
|
|
37
57
|
|
|
38
58
|
**If the user provided a file or URL to ingest:**
|
|
39
59
|
|
|
@@ -46,7 +66,7 @@ List un-ingested sources from the script output and ask which one to process now
|
|
|
46
66
|
|
|
47
67
|
---
|
|
48
68
|
|
|
49
|
-
## Step
|
|
69
|
+
## Step 5 — Synthesize a source-summary page
|
|
50
70
|
|
|
51
71
|
For the chosen source, read its content and create `pages/<slug>.md` with the following frontmatter:
|
|
52
72
|
|
|
@@ -70,7 +90,7 @@ Cross-reference existing pages with `[[wikilink]]` syntax where relevant.
|
|
|
70
90
|
|
|
71
91
|
---
|
|
72
92
|
|
|
73
|
-
## Step
|
|
93
|
+
## Step 6 — Update log.md
|
|
74
94
|
|
|
75
95
|
Append an ingest entry to `<wiki-root>/log.md`:
|
|
76
96
|
|
|
@@ -81,3 +101,7 @@ Append an ingest entry to `<wiki-root>/log.md`:
|
|
|
81
101
|
- summary: pages/<slug>.md
|
|
82
102
|
- tags: <tags>
|
|
83
103
|
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
> **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score — see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
|
package/skills/lint/SKILL.md
CHANGED
|
@@ -53,3 +53,7 @@ For **broken wikilinks**: list the affected files and ask if the user wants help
|
|
|
53
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
54
|
|
|
55
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]]`. The observability audit counts citations toward the autonomy score — see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
|
package/skills/query/SKILL.md
CHANGED
|
@@ -56,3 +56,7 @@ Read the top matching pages (up to 5) and produce a synthesized response:
|
|
|
56
56
|
3. **Gaps** — if the wiki lacks coverage on the topic, note what is missing and suggest an ingest target.
|
|
57
57
|
|
|
58
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]]`. The observability audit counts citations toward the autonomy score — see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
|
package/skills/verify/SKILL.md
CHANGED
|
@@ -90,3 +90,7 @@ updated: <today YYYY-MM-DD>
|
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
3. Save the file. Do not remove or reorder existing entries.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
> **Citation convention.** When you reference a wiki page in your response, link it as `[[page-slug]]`. The observability audit counts citations toward the autonomy score — see [[pages/observability/_index]] (run `/hypo:audit` to inspect).
|
package/templates/.hypoignore
CHANGED
|
@@ -8,10 +8,27 @@
|
|
|
8
8
|
*.zip
|
|
9
9
|
*.tar.gz
|
|
10
10
|
|
|
11
|
-
# Credentials and secrets
|
|
11
|
+
# Credentials and secrets (§6.8 baseline)
|
|
12
12
|
*.pem
|
|
13
13
|
*.key
|
|
14
|
-
|
|
14
|
+
.env*
|
|
15
|
+
*.p12
|
|
16
|
+
*.pfx
|
|
17
|
+
id_rsa*
|
|
18
|
+
id_ed25519*
|
|
19
|
+
*credentials*
|
|
20
|
+
*secret*
|
|
21
|
+
*token*
|
|
22
|
+
*password*
|
|
23
|
+
*passwd*
|
|
24
|
+
|
|
25
|
+
# Build artifacts
|
|
26
|
+
node_modules/
|
|
27
|
+
dist/
|
|
28
|
+
build/
|
|
29
|
+
|
|
30
|
+
# Hypomnema runtime cache (session growth metrics, future index.jsonl, etc.)
|
|
31
|
+
.cache/
|
|
15
32
|
|
|
16
33
|
# Draft / scratch (uncomment to hide from hooks)
|
|
17
34
|
# drafts/
|
package/templates/Home.md
CHANGED
|
@@ -19,6 +19,8 @@ tags: [wiki, home]
|
|
|
19
19
|
- [[SCHEMA]] — type system reference
|
|
20
20
|
- [[hypo-guide]] — operations guide
|
|
21
21
|
|
|
22
|
+
> **Auto-ingest rule**: after any `WebFetch` / `WebSearch` that yields relevant knowledge, run `/hypo:ingest` immediately without waiting for an explicit request.
|
|
23
|
+
|
|
22
24
|
---
|
|
23
25
|
|
|
24
26
|
## Active Projects
|
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:
|
|
5
|
+
version: 2.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Wiki Schema
|
|
@@ -61,6 +61,17 @@ Files responsible for session continuity — separate from the type taxonomy abo
|
|
|
61
61
|
- Session start: root `hot.md` → project `hot.md` → `session-state.md`
|
|
62
62
|
- Session close: update project `hot.md` (what was done) + `session-state.md` (what to do next) + root `hot.md`
|
|
63
63
|
|
|
64
|
+
**Pointer table row format** (`hypo-hot-rebuild.mjs` parses this with a fixed regex — wrong col3 format causes the row to be silently skipped):
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
| <Project Name> | YYYY-MM-DD | [[projects/<slug>/hot]] |
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Column semantics:
|
|
71
|
+
- col 1 (name): preserved as-is from the existing row
|
|
72
|
+
- col 2 (date): **ignored on read** — rebuilt from `projects/<slug>/hot.md` frontmatter `updated:` (falls back to today if the file is absent)
|
|
73
|
+
- col 3 (wikilink): must be `[[projects/<slug>/hot]]` — no trailing path, no markdown link `[text](url)`. Extra trailing columns are discarded during canonical rebuild (not dropped).
|
|
74
|
+
|
|
64
75
|
---
|
|
65
76
|
|
|
66
77
|
## 3. Required Frontmatter Fields
|
|
@@ -85,15 +96,59 @@ verify_by: <question to re-check at next review>
|
|
|
85
96
|
verify_by_date: YYYY-MM-DD
|
|
86
97
|
```
|
|
87
98
|
|
|
99
|
+
### 3.1. `feedback` type — projection fields (ADR 0031)
|
|
100
|
+
|
|
101
|
+
Feedback pages are the single source of truth for behavior corrections;
|
|
102
|
+
`hypomnema feedback-sync` projects them one-way into Claude Code's `MEMORY.md`
|
|
103
|
+
and `~/.claude/CLAUDE.md` `<learned_behaviors>`. They require:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
status: active | superseded | archived
|
|
107
|
+
scope: global | project:<project-id> # global → eligible for CLAUDE.md projection;
|
|
108
|
+
# <project-id> must exact-match the resolved
|
|
109
|
+
# project-id (default: cwd → `/`,`.` replaced
|
|
110
|
+
# with `-`; or pass `--project-id=<id>`).
|
|
111
|
+
# Mismatched scope = page is NOT projected
|
|
112
|
+
# into that project's MEMORY.md.
|
|
113
|
+
tier: L1 | L2 # L1 required for CLAUDE.md <learned_behaviors>
|
|
114
|
+
targets: [project-memory, claude-learned] # which projection surfaces to derive
|
|
115
|
+
sensitivity: public | sanitized # `private` forbidden (wiki is git-pushed)
|
|
116
|
+
priority: 1-5 # over-cap sort key (higher first)
|
|
117
|
+
memory_summary: <one line for the MEMORY.md index>
|
|
118
|
+
reason: <why this rule is needed>
|
|
119
|
+
source: session:YYYY-MM-DD | commit:<hash> | pr:<n> | https://...
|
|
120
|
+
|
|
121
|
+
# conditional — required when `targets` includes `claude-learned`:
|
|
122
|
+
global_summary: <one line for the <learned_behaviors> entry>
|
|
123
|
+
promote_to_global: true # explicit opt-in to global projection
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Edit the feedback page only — never hand-edit the generated
|
|
127
|
+
`<!-- HYPO:FEEDBACK-SYNC:START … -->` managed blocks (sync detects tampering as a conflict).
|
|
128
|
+
|
|
88
129
|
---
|
|
89
130
|
|
|
90
131
|
## 4. Tag Vocabulary
|
|
91
132
|
|
|
92
|
-
Use lowercase, hyphenated tags.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
**Meta
|
|
133
|
+
Use lowercase, hyphenated tags. Vocabulary is locked — `lint` blocks unknown tags
|
|
134
|
+
and forbidden patterns (PascalCase, plurals, whitespace, generic words).
|
|
135
|
+
Extend this list (and `~/hypomnema/SCHEMA.md`) before introducing a new tag.
|
|
136
|
+
|
|
137
|
+
**Meta**: `wiki`, `index`, `pages`, `home`, `overview`, `guide`, `operations`, `schema`, `reference`, `hypo`, `commands`, `hot-cache`, `migration`
|
|
138
|
+
**Workflow**: `automation`, `hooks`, `observability`, `autonomy`, `wiki-health`, `weekly`
|
|
139
|
+
**Project**: `project`, `prd`, `adr`, `session-state`
|
|
140
|
+
**Domain**: `ai`, `dev`, `ops`, `security`, `data`, `design`, `management`
|
|
141
|
+
**Status**: `active`, `completed`, `archived`, `draft`, `stable`, `deprecated`, `needs-review`, `proposed`, `superseded`
|
|
142
|
+
**Content classification**: `learning`, `tip`, `feedback`, `gotcha`, `concept`, `pattern`
|
|
143
|
+
|
|
144
|
+
### Forbidden patterns
|
|
145
|
+
|
|
146
|
+
| Pattern | Reason | Use instead |
|
|
147
|
+
|---------|--------|-------------|
|
|
148
|
+
| PascalCase (`Jenkins`, `Claude`) | Inconsistent casing | `jenkins`, `claude-code` |
|
|
149
|
+
| Plurals (`learnings`, `tips`) | Singular form is canonical | `learning`, `tip` |
|
|
150
|
+
| Generic (`general`, `misc`, `other`, `todo`) | No search value | Specific domain tag |
|
|
151
|
+
| Whitespace (`llm wiki`) | Parse breakage | `llm-wiki` |
|
|
97
152
|
|
|
98
153
|
---
|
|
99
154
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/templates/hot.md
CHANGED
|
@@ -14,6 +14,8 @@ tags: [wiki, operations]
|
|
|
14
14
|
|
|
15
15
|
| Project | Last Session | Hot Cache |
|
|
16
16
|
|---|---|---|
|
|
17
|
+
<!-- Row format: | Project Name | YYYY-MM-DD | [[projects/slug/hot]] | -->
|
|
18
|
+
<!-- col2 date is rebuilt from projects/<slug>/hot.md frontmatter on each session close -->
|
|
17
19
|
|
|
18
20
|
## Session Start Checklist
|
|
19
21
|
|