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
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
const SESSION_LOG_DATE_RE = /^## \[(\d{4}-\d{2}-\d{2})\]/gm;
|
|
5
|
+
const DESIGN_HISTORY_DATE_RE = /^## (\d{4}-\d{2}-\d{2})/gm;
|
|
6
|
+
|
|
7
|
+
function parseDates(text, pattern) {
|
|
8
|
+
const dates = [];
|
|
9
|
+
pattern.lastIndex = 0;
|
|
10
|
+
let m;
|
|
11
|
+
while ((m = pattern.exec(text)) !== null) {
|
|
12
|
+
// The regex matches digit-shaped YYYY-MM-DD literals but cannot reject
|
|
13
|
+
// semantically invalid ones like 2026-13-01 or 2026-02-30. JavaScript's
|
|
14
|
+
// Date constructor returns an Invalid Date for those, which would later
|
|
15
|
+
// crash `toISOString()` with RangeError and poison `>` comparisons inside
|
|
16
|
+
// maxDate. Filter at the parse boundary so callers never see one.
|
|
17
|
+
const d = new Date(m[1]);
|
|
18
|
+
if (!Number.isNaN(d.getTime())) dates.push(d);
|
|
19
|
+
}
|
|
20
|
+
return dates;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function maxDate(dates) {
|
|
24
|
+
if (dates.length === 0) return null;
|
|
25
|
+
return dates.reduce((a, b) => (a > b ? a : b));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Returns stale findings: { project, lastSession, lastDesignHistory, diffDays }
|
|
29
|
+
// Only includes projects where design-history.md exists AND is stale relative
|
|
30
|
+
// to the latest session-log entry. Date source is body section headings
|
|
31
|
+
// (## YYYY-MM-DD), not frontmatter `updated:` — auto-stage hooks bump the
|
|
32
|
+
// frontmatter on unrelated edits, so it can't signal staleness on its own.
|
|
33
|
+
export function findDesignHistoryStale(hypoDir) {
|
|
34
|
+
const stale = [];
|
|
35
|
+
|
|
36
|
+
const projectsDir = join(hypoDir, 'projects');
|
|
37
|
+
if (!existsSync(projectsDir)) return stale;
|
|
38
|
+
|
|
39
|
+
for (const name of readdirSync(projectsDir)) {
|
|
40
|
+
const projectDir = join(projectsDir, name);
|
|
41
|
+
if (!statSync(projectDir).isDirectory()) continue;
|
|
42
|
+
|
|
43
|
+
const dhPath = join(projectDir, 'design-history.md');
|
|
44
|
+
if (!existsSync(dhPath)) continue;
|
|
45
|
+
|
|
46
|
+
// session-log can live as a flat `session-log.md` (legacy) or a directory
|
|
47
|
+
// `session-log/YYYY-MM.md` (spec §5.2.7 canonical). Aggregate dates from
|
|
48
|
+
// whichever shape is present — both forms appear in the wild and the
|
|
49
|
+
// staleness check needs to see all of them.
|
|
50
|
+
const sessionDates = [];
|
|
51
|
+
const flatSlPath = join(projectDir, 'session-log.md');
|
|
52
|
+
if (existsSync(flatSlPath)) {
|
|
53
|
+
sessionDates.push(...parseDates(readFileSync(flatSlPath, 'utf-8'), SESSION_LOG_DATE_RE));
|
|
54
|
+
}
|
|
55
|
+
const dirSlPath = join(projectDir, 'session-log');
|
|
56
|
+
if (existsSync(dirSlPath) && statSync(dirSlPath).isDirectory()) {
|
|
57
|
+
for (const entry of readdirSync(dirSlPath)) {
|
|
58
|
+
if (!entry.endsWith('.md')) continue;
|
|
59
|
+
const text = readFileSync(join(dirSlPath, entry), 'utf-8');
|
|
60
|
+
sessionDates.push(...parseDates(text, SESSION_LOG_DATE_RE));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (sessionDates.length === 0) continue;
|
|
64
|
+
|
|
65
|
+
const dhText = readFileSync(dhPath, 'utf-8');
|
|
66
|
+
const lastSession = maxDate(sessionDates);
|
|
67
|
+
const lastDH = maxDate(parseDates(dhText, DESIGN_HISTORY_DATE_RE));
|
|
68
|
+
|
|
69
|
+
if (!lastSession) continue;
|
|
70
|
+
|
|
71
|
+
if (!lastDH || lastSession > lastDH) {
|
|
72
|
+
const diffDays = lastDH ? Math.round((lastSession - lastDH) / (1000 * 60 * 60 * 24)) : null;
|
|
73
|
+
stale.push({
|
|
74
|
+
project: name,
|
|
75
|
+
lastSession: lastSession.toISOString().slice(0, 10),
|
|
76
|
+
lastDesignHistory: lastDH ? lastDH.toISOString().slice(0, 10) : '(없음)',
|
|
77
|
+
diffDays,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return stale;
|
|
83
|
+
}
|