engrm 0.1.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 (82) hide show
  1. package/.mcp.json +9 -0
  2. package/AUTH-DESIGN.md +436 -0
  3. package/BRIEF.md +197 -0
  4. package/CLAUDE.md +44 -0
  5. package/COMPETITIVE.md +174 -0
  6. package/CONTEXT-OPTIMIZATION.md +305 -0
  7. package/INFRASTRUCTURE.md +252 -0
  8. package/LICENSE +105 -0
  9. package/MARKET.md +230 -0
  10. package/PLAN.md +278 -0
  11. package/README.md +121 -0
  12. package/SENTINEL.md +293 -0
  13. package/SERVER-API-PLAN.md +553 -0
  14. package/SPEC.md +843 -0
  15. package/SWOT.md +148 -0
  16. package/SYNC-ARCHITECTURE.md +294 -0
  17. package/VIBE-CODER-STRATEGY.md +250 -0
  18. package/bun.lock +375 -0
  19. package/hooks/post-tool-use.ts +144 -0
  20. package/hooks/session-start.ts +64 -0
  21. package/hooks/stop.ts +131 -0
  22. package/mem-page.html +1305 -0
  23. package/package.json +30 -0
  24. package/src/capture/dedup.test.ts +103 -0
  25. package/src/capture/dedup.ts +76 -0
  26. package/src/capture/extractor.test.ts +245 -0
  27. package/src/capture/extractor.ts +330 -0
  28. package/src/capture/quality.test.ts +168 -0
  29. package/src/capture/quality.ts +104 -0
  30. package/src/capture/retrospective.test.ts +115 -0
  31. package/src/capture/retrospective.ts +121 -0
  32. package/src/capture/scanner.test.ts +131 -0
  33. package/src/capture/scanner.ts +100 -0
  34. package/src/capture/scrubber.test.ts +144 -0
  35. package/src/capture/scrubber.ts +181 -0
  36. package/src/cli.ts +517 -0
  37. package/src/config.ts +238 -0
  38. package/src/context/inject.test.ts +940 -0
  39. package/src/context/inject.ts +382 -0
  40. package/src/embeddings/backfill.ts +50 -0
  41. package/src/embeddings/embedder.test.ts +76 -0
  42. package/src/embeddings/embedder.ts +139 -0
  43. package/src/lifecycle/aging.test.ts +103 -0
  44. package/src/lifecycle/aging.ts +36 -0
  45. package/src/lifecycle/compaction.test.ts +264 -0
  46. package/src/lifecycle/compaction.ts +190 -0
  47. package/src/lifecycle/purge.test.ts +100 -0
  48. package/src/lifecycle/purge.ts +37 -0
  49. package/src/lifecycle/scheduler.test.ts +120 -0
  50. package/src/lifecycle/scheduler.ts +101 -0
  51. package/src/provisioning/browser-auth.ts +172 -0
  52. package/src/provisioning/provision.test.ts +198 -0
  53. package/src/provisioning/provision.ts +94 -0
  54. package/src/register.test.ts +167 -0
  55. package/src/register.ts +178 -0
  56. package/src/server.ts +436 -0
  57. package/src/storage/migrations.test.ts +244 -0
  58. package/src/storage/migrations.ts +261 -0
  59. package/src/storage/outbox.test.ts +229 -0
  60. package/src/storage/outbox.ts +131 -0
  61. package/src/storage/projects.test.ts +137 -0
  62. package/src/storage/projects.ts +184 -0
  63. package/src/storage/sqlite.test.ts +798 -0
  64. package/src/storage/sqlite.ts +934 -0
  65. package/src/storage/vec.test.ts +198 -0
  66. package/src/sync/auth.test.ts +76 -0
  67. package/src/sync/auth.ts +68 -0
  68. package/src/sync/client.ts +183 -0
  69. package/src/sync/engine.test.ts +94 -0
  70. package/src/sync/engine.ts +127 -0
  71. package/src/sync/pull.test.ts +279 -0
  72. package/src/sync/pull.ts +170 -0
  73. package/src/sync/push.test.ts +117 -0
  74. package/src/sync/push.ts +230 -0
  75. package/src/tools/get.ts +34 -0
  76. package/src/tools/pin.ts +47 -0
  77. package/src/tools/save.test.ts +301 -0
  78. package/src/tools/save.ts +231 -0
  79. package/src/tools/search.test.ts +69 -0
  80. package/src/tools/search.ts +181 -0
  81. package/src/tools/timeline.ts +64 -0
  82. package/tsconfig.json +22 -0
package/hooks/stop.ts ADDED
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Stop hook for Claude Code.
4
+ *
5
+ * Fires when Claude finishes responding. We:
6
+ * 1. Check stop_hook_active to prevent infinite loops
7
+ * 2. Complete the session in the database
8
+ * 3. Generate a retrospective summary from session observations
9
+ * 4. Exit 0 to allow Claude to stop
10
+ */
11
+
12
+ import { loadConfig, configExists, getDbPath } from "../src/config.js";
13
+ import { MemDatabase } from "../src/storage/sqlite.js";
14
+ import { extractRetrospective } from "../src/capture/retrospective.js";
15
+
16
+ import type { InsertSessionSummary } from "../src/storage/sqlite.js";
17
+
18
+ function printRetrospective(summary: InsertSessionSummary): void {
19
+ const lines: string[] = [];
20
+ lines.push("");
21
+ lines.push("━━━ Engrm Session Summary ━━━");
22
+ lines.push("");
23
+
24
+ if (summary.request) {
25
+ lines.push(`📋 Request: ${summary.request}`);
26
+ lines.push("");
27
+ }
28
+ if (summary.investigated) {
29
+ lines.push("🔍 Investigated:");
30
+ lines.push(summary.investigated);
31
+ lines.push("");
32
+ }
33
+ if (summary.learned) {
34
+ lines.push("💡 Learned:");
35
+ lines.push(summary.learned);
36
+ lines.push("");
37
+ }
38
+ if (summary.completed) {
39
+ lines.push("✅ Completed:");
40
+ lines.push(summary.completed);
41
+ lines.push("");
42
+ }
43
+ if (summary.next_steps) {
44
+ lines.push("➡️ Next Steps:");
45
+ lines.push(summary.next_steps);
46
+ lines.push("");
47
+ }
48
+
49
+ lines.push("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
50
+ console.log(lines.join("\n"));
51
+ }
52
+
53
+ interface StopEvent {
54
+ session_id: string;
55
+ hook_event_name: string;
56
+ stop_hook_active: boolean;
57
+ last_assistant_message: string;
58
+ cwd: string;
59
+ }
60
+
61
+ async function main(): Promise<void> {
62
+ // Read stdin
63
+ const chunks: string[] = [];
64
+ for await (const chunk of Bun.stdin.stream()) {
65
+ chunks.push(new TextDecoder().decode(chunk));
66
+ }
67
+ const raw = chunks.join("");
68
+
69
+ if (!raw.trim()) process.exit(0);
70
+
71
+ let event: StopEvent;
72
+ try {
73
+ event = JSON.parse(raw) as StopEvent;
74
+ } catch {
75
+ process.exit(0);
76
+ }
77
+
78
+ // Prevent infinite loops — if we're already in a stop hook, just exit
79
+ if (event.stop_hook_active) process.exit(0);
80
+
81
+ // Bail if not configured
82
+ if (!configExists()) process.exit(0);
83
+
84
+ let config;
85
+ let db;
86
+ try {
87
+ config = loadConfig();
88
+ db = new MemDatabase(getDbPath());
89
+ } catch {
90
+ process.exit(0);
91
+ }
92
+
93
+ try {
94
+ // Complete the session
95
+ if (event.session_id) {
96
+ db.completeSession(event.session_id);
97
+
98
+ // Generate retrospective — only if we haven't already for this session
99
+ const existing = db.getSessionSummary(event.session_id);
100
+ if (!existing) {
101
+ const observations = db.getObservationsBySession(event.session_id);
102
+ if (observations.length > 0) {
103
+ const session = db.getSessionMetrics(event.session_id);
104
+ const summary = extractRetrospective(
105
+ observations,
106
+ event.session_id,
107
+ session?.project_id ?? null,
108
+ config.user_id
109
+ );
110
+
111
+ if (summary) {
112
+ const row = db.insertSessionSummary(summary);
113
+ db.addToOutbox("summary", row.id);
114
+
115
+ // Display session retrospective to the user
116
+ printRetrospective(summary);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ } finally {
122
+ db.close();
123
+ }
124
+
125
+ // Exit 0 — allow Claude to stop
126
+ process.exit(0);
127
+ }
128
+
129
+ main().catch(() => {
130
+ process.exit(0);
131
+ });