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.
Files changed (76) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ko.md +12 -5
  4. package/README.md +12 -5
  5. package/commands/audit.md +46 -0
  6. package/commands/crystallize.md +113 -23
  7. package/commands/feedback.md +40 -26
  8. package/commands/ingest.md +31 -9
  9. package/commands/upgrade.md +2 -2
  10. package/docs/ARCHITECTURE.md +83 -9
  11. package/docs/CONTRIBUTING.md +2 -2
  12. package/hooks/hooks.json +39 -1
  13. package/hooks/hypo-auto-commit.mjs +23 -4
  14. package/hooks/hypo-auto-minimal-crystallize.mjs +145 -0
  15. package/hooks/hypo-auto-stage.mjs +9 -5
  16. package/hooks/hypo-compact-guard.mjs +33 -24
  17. package/hooks/hypo-cwd-change.mjs +107 -24
  18. package/hooks/hypo-file-watch.mjs +23 -10
  19. package/hooks/hypo-first-prompt.mjs +37 -23
  20. package/hooks/hypo-hot-rebuild.mjs +31 -8
  21. package/hooks/hypo-lookup.mjs +171 -65
  22. package/hooks/hypo-personal-check.mjs +207 -112
  23. package/hooks/hypo-pre-commit.mjs +46 -0
  24. package/hooks/hypo-session-end.mjs +58 -0
  25. package/hooks/hypo-session-record.mjs +60 -0
  26. package/hooks/hypo-session-start.mjs +312 -44
  27. package/hooks/hypo-shared.mjs +880 -28
  28. package/hooks/hypo-web-fetch-ingest.mjs +121 -0
  29. package/hooks/version-check-fetch.mjs +74 -0
  30. package/hooks/version-check.mjs +184 -0
  31. package/package.json +17 -3
  32. package/scripts/crystallize.mjs +623 -18
  33. package/scripts/doctor.mjs +739 -46
  34. package/scripts/feedback-sync.mjs +974 -0
  35. package/scripts/feedback.mjs +253 -44
  36. package/scripts/graph.mjs +35 -22
  37. package/scripts/ingest.mjs +89 -16
  38. package/scripts/init.mjs +442 -114
  39. package/scripts/lib/design-history-stale.mjs +83 -0
  40. package/scripts/lib/extensions.mjs +749 -0
  41. package/scripts/lib/frontmatter.mjs +5 -1
  42. package/scripts/lib/hypo-ignore.mjs +12 -10
  43. package/scripts/lib/pkg-json.mjs +23 -5
  44. package/scripts/lib/project-create.mjs +225 -0
  45. package/scripts/lib/schema-vocab.mjs +96 -0
  46. package/scripts/lint.mjs +238 -31
  47. package/scripts/query.mjs +26 -10
  48. package/scripts/resume.mjs +11 -5
  49. package/scripts/session-audit.mjs +277 -0
  50. package/scripts/smoke-pack.mjs +224 -0
  51. package/scripts/stats.mjs +24 -10
  52. package/scripts/uninstall.mjs +369 -48
  53. package/scripts/upgrade.mjs +766 -195
  54. package/scripts/verify.mjs +24 -14
  55. package/scripts/weekly-report.mjs +211 -0
  56. package/skills/crystallize/SKILL.md +24 -7
  57. package/skills/graph/SKILL.md +4 -0
  58. package/skills/ingest/SKILL.md +29 -5
  59. package/skills/lint/SKILL.md +4 -0
  60. package/skills/query/SKILL.md +4 -0
  61. package/skills/verify/SKILL.md +4 -0
  62. package/templates/.hypoignore +19 -2
  63. package/templates/Home.md +2 -0
  64. package/templates/SCHEMA.md +61 -6
  65. package/templates/extensions/agents/.gitkeep +0 -0
  66. package/templates/extensions/commands/.gitkeep +0 -0
  67. package/templates/extensions/hooks/.gitkeep +0 -0
  68. package/templates/extensions/skills/.gitkeep +0 -0
  69. package/templates/gitignore +5 -0
  70. package/templates/hot.md +2 -0
  71. package/templates/hypo-config.md +1 -1
  72. package/templates/hypo-guide.md +63 -1
  73. package/templates/hypo-help.md +1 -1
  74. package/templates/pages/observability/_index.md +77 -0
  75. package/templates/projects/_template/index.md +2 -2
  76. 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
+ }