@mneme-ai/core 0.9.0 → 0.11.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 (181) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/insights/bus-factor.d.ts +58 -0
  6. package/dist/insights/bus-factor.d.ts.map +1 -0
  7. package/dist/insights/bus-factor.js +117 -0
  8. package/dist/insights/bus-factor.js.map +1 -0
  9. package/dist/insights/bus-factor.test.d.ts +2 -0
  10. package/dist/insights/bus-factor.test.d.ts.map +1 -0
  11. package/dist/insights/bus-factor.test.js +149 -0
  12. package/dist/insights/bus-factor.test.js.map +1 -0
  13. package/dist/insights/commit-coach.d.ts +80 -0
  14. package/dist/insights/commit-coach.d.ts.map +1 -0
  15. package/dist/insights/commit-coach.js +230 -0
  16. package/dist/insights/commit-coach.js.map +1 -0
  17. package/dist/insights/commit-coach.test.d.ts +2 -0
  18. package/dist/insights/commit-coach.test.d.ts.map +1 -0
  19. package/dist/insights/commit-coach.test.js +163 -0
  20. package/dist/insights/commit-coach.test.js.map +1 -0
  21. package/dist/insights/crystal-ball.d.ts +76 -0
  22. package/dist/insights/crystal-ball.d.ts.map +1 -0
  23. package/dist/insights/crystal-ball.js +219 -0
  24. package/dist/insights/crystal-ball.js.map +1 -0
  25. package/dist/insights/crystal-ball.test.d.ts +2 -0
  26. package/dist/insights/crystal-ball.test.d.ts.map +1 -0
  27. package/dist/insights/crystal-ball.test.js +157 -0
  28. package/dist/insights/crystal-ball.test.js.map +1 -0
  29. package/dist/insights/ghost.d.ts +80 -0
  30. package/dist/insights/ghost.d.ts.map +1 -0
  31. package/dist/insights/ghost.js +138 -0
  32. package/dist/insights/ghost.js.map +1 -0
  33. package/dist/insights/ghost.test.d.ts +2 -0
  34. package/dist/insights/ghost.test.d.ts.map +1 -0
  35. package/dist/insights/ghost.test.js +141 -0
  36. package/dist/insights/ghost.test.js.map +1 -0
  37. package/dist/insights/index.d.ts +8 -0
  38. package/dist/insights/index.d.ts.map +1 -1
  39. package/dist/insights/index.js +8 -0
  40. package/dist/insights/index.js.map +1 -1
  41. package/dist/insights/paradox.d.ts +36 -0
  42. package/dist/insights/paradox.d.ts.map +1 -0
  43. package/dist/insights/paradox.js +201 -0
  44. package/dist/insights/paradox.js.map +1 -0
  45. package/dist/insights/paradox.test.d.ts +2 -0
  46. package/dist/insights/paradox.test.d.ts.map +1 -0
  47. package/dist/insights/paradox.test.js +88 -0
  48. package/dist/insights/paradox.test.js.map +1 -0
  49. package/dist/insights/premortem.d.ts +73 -0
  50. package/dist/insights/premortem.d.ts.map +1 -0
  51. package/dist/insights/premortem.js +209 -0
  52. package/dist/insights/premortem.js.map +1 -0
  53. package/dist/insights/premortem.test.d.ts +2 -0
  54. package/dist/insights/premortem.test.d.ts.map +1 -0
  55. package/dist/insights/premortem.test.js +169 -0
  56. package/dist/insights/premortem.test.js.map +1 -0
  57. package/dist/insights/regret.d.ts +57 -0
  58. package/dist/insights/regret.d.ts.map +1 -0
  59. package/dist/insights/regret.js +137 -0
  60. package/dist/insights/regret.js.map +1 -0
  61. package/dist/insights/regret.test.d.ts +2 -0
  62. package/dist/insights/regret.test.d.ts.map +1 -0
  63. package/dist/insights/regret.test.js +153 -0
  64. package/dist/insights/regret.test.js.map +1 -0
  65. package/dist/insights/time-machine.d.ts +70 -0
  66. package/dist/insights/time-machine.d.ts.map +1 -0
  67. package/dist/insights/time-machine.js +177 -0
  68. package/dist/insights/time-machine.js.map +1 -0
  69. package/dist/insights/time-machine.test.d.ts +2 -0
  70. package/dist/insights/time-machine.test.d.ts.map +1 -0
  71. package/dist/insights/time-machine.test.js +141 -0
  72. package/dist/insights/time-machine.test.js.map +1 -0
  73. package/dist/insights/who-knows.d.ts +18 -0
  74. package/dist/insights/who-knows.d.ts.map +1 -1
  75. package/dist/insights/who-knows.js +29 -0
  76. package/dist/insights/who-knows.js.map +1 -1
  77. package/dist/insights/who-knows.test.js +63 -1
  78. package/dist/insights/who-knows.test.js.map +1 -1
  79. package/dist/quant/alpha.d.ts +87 -0
  80. package/dist/quant/alpha.d.ts.map +1 -0
  81. package/dist/quant/alpha.js +103 -0
  82. package/dist/quant/alpha.js.map +1 -0
  83. package/dist/quant/alpha.test.d.ts +2 -0
  84. package/dist/quant/alpha.test.d.ts.map +1 -0
  85. package/dist/quant/alpha.test.js +147 -0
  86. package/dist/quant/alpha.test.js.map +1 -0
  87. package/dist/quant/backtest.d.ts +57 -0
  88. package/dist/quant/backtest.d.ts.map +1 -0
  89. package/dist/quant/backtest.js +90 -0
  90. package/dist/quant/backtest.js.map +1 -0
  91. package/dist/quant/backtest.test.d.ts +2 -0
  92. package/dist/quant/backtest.test.d.ts.map +1 -0
  93. package/dist/quant/backtest.test.js +133 -0
  94. package/dist/quant/backtest.test.js.map +1 -0
  95. package/dist/quant/black-swan.d.ts +45 -0
  96. package/dist/quant/black-swan.d.ts.map +1 -0
  97. package/dist/quant/black-swan.js +112 -0
  98. package/dist/quant/black-swan.js.map +1 -0
  99. package/dist/quant/black-swan.test.d.ts +2 -0
  100. package/dist/quant/black-swan.test.d.ts.map +1 -0
  101. package/dist/quant/black-swan.test.js +131 -0
  102. package/dist/quant/black-swan.test.js.map +1 -0
  103. package/dist/quant/correlation-matrix.d.ts +54 -0
  104. package/dist/quant/correlation-matrix.d.ts.map +1 -0
  105. package/dist/quant/correlation-matrix.js +103 -0
  106. package/dist/quant/correlation-matrix.js.map +1 -0
  107. package/dist/quant/correlation-matrix.test.d.ts +2 -0
  108. package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
  109. package/dist/quant/correlation-matrix.test.js +118 -0
  110. package/dist/quant/correlation-matrix.test.js.map +1 -0
  111. package/dist/quant/drawdown.d.ts +51 -0
  112. package/dist/quant/drawdown.d.ts.map +1 -0
  113. package/dist/quant/drawdown.js +96 -0
  114. package/dist/quant/drawdown.js.map +1 -0
  115. package/dist/quant/drawdown.test.d.ts +2 -0
  116. package/dist/quant/drawdown.test.d.ts.map +1 -0
  117. package/dist/quant/drawdown.test.js +166 -0
  118. package/dist/quant/drawdown.test.js.map +1 -0
  119. package/dist/quant/greek.d.ts +55 -0
  120. package/dist/quant/greek.d.ts.map +1 -0
  121. package/dist/quant/greek.js +157 -0
  122. package/dist/quant/greek.js.map +1 -0
  123. package/dist/quant/greek.test.d.ts +2 -0
  124. package/dist/quant/greek.test.d.ts.map +1 -0
  125. package/dist/quant/greek.test.js +138 -0
  126. package/dist/quant/greek.test.js.map +1 -0
  127. package/dist/quant/implied-volatility.d.ts +65 -0
  128. package/dist/quant/implied-volatility.d.ts.map +1 -0
  129. package/dist/quant/implied-volatility.js +149 -0
  130. package/dist/quant/implied-volatility.js.map +1 -0
  131. package/dist/quant/implied-volatility.test.d.ts +2 -0
  132. package/dist/quant/implied-volatility.test.d.ts.map +1 -0
  133. package/dist/quant/implied-volatility.test.js +127 -0
  134. package/dist/quant/implied-volatility.test.js.map +1 -0
  135. package/dist/quant/index.d.ts +28 -0
  136. package/dist/quant/index.d.ts.map +1 -0
  137. package/dist/quant/index.js +28 -0
  138. package/dist/quant/index.js.map +1 -0
  139. package/dist/quant/insider-trading.d.ts +56 -0
  140. package/dist/quant/insider-trading.d.ts.map +1 -0
  141. package/dist/quant/insider-trading.js +129 -0
  142. package/dist/quant/insider-trading.js.map +1 -0
  143. package/dist/quant/insider-trading.test.d.ts +2 -0
  144. package/dist/quant/insider-trading.test.d.ts.map +1 -0
  145. package/dist/quant/insider-trading.test.js +130 -0
  146. package/dist/quant/insider-trading.test.js.map +1 -0
  147. package/dist/quant/moneyball.d.ts +48 -0
  148. package/dist/quant/moneyball.d.ts.map +1 -0
  149. package/dist/quant/moneyball.js +110 -0
  150. package/dist/quant/moneyball.js.map +1 -0
  151. package/dist/quant/moneyball.test.d.ts +2 -0
  152. package/dist/quant/moneyball.test.d.ts.map +1 -0
  153. package/dist/quant/moneyball.test.js +137 -0
  154. package/dist/quant/moneyball.test.js.map +1 -0
  155. package/dist/quant/tax-loss-harvest.d.ts +59 -0
  156. package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
  157. package/dist/quant/tax-loss-harvest.js +126 -0
  158. package/dist/quant/tax-loss-harvest.js.map +1 -0
  159. package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
  160. package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
  161. package/dist/quant/tax-loss-harvest.test.js +126 -0
  162. package/dist/quant/tax-loss-harvest.test.js.map +1 -0
  163. package/dist/retrieve/synthesize.d.ts.map +1 -1
  164. package/dist/retrieve/synthesize.js +56 -25
  165. package/dist/retrieve/synthesize.js.map +1 -1
  166. package/dist/retrieve/synthesize.test.js +26 -15
  167. package/dist/retrieve/synthesize.test.js.map +1 -1
  168. package/dist/store/schema.d.ts +2 -2
  169. package/dist/store/schema.d.ts.map +1 -1
  170. package/dist/store/schema.js +6 -2
  171. package/dist/store/schema.js.map +1 -1
  172. package/dist/store/sqlite.d.ts +2 -0
  173. package/dist/store/sqlite.d.ts.map +1 -1
  174. package/dist/store/sqlite.js +24 -0
  175. package/dist/store/sqlite.js.map +1 -1
  176. package/dist/store/sqlite.test.js +1 -1
  177. package/dist/util/index.d.ts +4 -0
  178. package/dist/util/index.d.ts.map +1 -1
  179. package/dist/util/index.js +26 -0
  180. package/dist/util/index.js.map +1 -1
  181. package/package.json +1 -1
@@ -0,0 +1,209 @@
1
+ const REVERT_RE = /\b(revert|reverts|reverted|rollback)\b/i;
2
+ const HOTFIX_RE = /\b(hotfix|emergency|urgent|critical|p[01])\b/i;
3
+ const INCIDENT_RE = /\b(incident|outage|down|broken|crash|regression)\b/i;
4
+ const REWRITE_RE = /\b(rewrite|rewritten|redo|redid|second(?:\s|-)attempt)\b/i;
5
+ const STOPWORDS = new Set([
6
+ "the", "a", "an", "of", "to", "in", "on", "for", "and", "or", "with", "add", "added", "fix",
7
+ "remove", "update", "change", "make", "made", "new", "old", "this", "that", "is", "are", "be",
8
+ "was", "were", "my", "our", "your", "i", "we", "you", "it", "its", "into", "from", "by", "at",
9
+ ]);
10
+ /**
11
+ * Score similarity between an intent string and a commit, by token overlap
12
+ * + a small bonus for files-mentioned matching commit-touched files.
13
+ */
14
+ export function scoreSimilarity(intent, c) {
15
+ const intentTokens = tokenize(intent);
16
+ if (intentTokens.size === 0)
17
+ return 0;
18
+ const commitText = `${c.subject}\n${c.body || ""}`;
19
+ const commitTokens = tokenize(commitText);
20
+ let overlap = 0;
21
+ for (const t of intentTokens)
22
+ if (commitTokens.has(t))
23
+ overlap += 1;
24
+ const base = overlap / intentTokens.size;
25
+ // Path-level boost: if intent names a file/path, check commit touches it.
26
+ const pathHint = extractPathHint(intent);
27
+ let pathBoost = 0;
28
+ if (pathHint && c.files?.some((f) => f.includes(pathHint))) {
29
+ pathBoost = 0.2;
30
+ }
31
+ return Math.min(1, base + pathBoost);
32
+ }
33
+ function tokenize(s) {
34
+ const out = new Set();
35
+ for (const t of s
36
+ .toLowerCase()
37
+ .replace(/[^a-z0-9_/.-]+/g, " ")
38
+ .split(/\s+/)) {
39
+ if (!t)
40
+ continue;
41
+ if (t.length < 3)
42
+ continue;
43
+ if (STOPWORDS.has(t))
44
+ continue;
45
+ out.add(t);
46
+ }
47
+ return out;
48
+ }
49
+ function extractPathHint(intent) {
50
+ // crude: any token that looks like a path or file
51
+ const m = intent.match(/[A-Za-z0-9_./-]+\.(?:ts|tsx|js|jsx|py|go|rs|java|md)/);
52
+ return m ? m[0] : null;
53
+ }
54
+ /**
55
+ * Build a premortem from an intent string and the commit history. Caller
56
+ * passes the full commit history (newest first or oldest first — we sort).
57
+ */
58
+ export function buildPremortem(intent, commits, opts = {}) {
59
+ const similarityFloor = opts.similarityFloor ?? 0.25;
60
+ const windowDays = opts.windowDays ?? 14;
61
+ const maxAttempts = opts.maxAttempts ?? 25;
62
+ const sorted = [...commits].sort((a, b) => a.authorDate.localeCompare(b.authorDate));
63
+ // Find similar past attempts. Exclude commits that are themselves regret
64
+ // signals (revert / hotfix / incident) — those aren't attempts, they're
65
+ // the consequence of one.
66
+ const scored = [];
67
+ for (const c of sorted) {
68
+ const subject = c.subject || "";
69
+ if (REVERT_RE.test(subject) || HOTFIX_RE.test(subject) || INCIDENT_RE.test(subject)) {
70
+ continue;
71
+ }
72
+ const s = scoreSimilarity(intent, c);
73
+ if (s >= similarityFloor)
74
+ scored.push({ commit: c, similarity: s });
75
+ }
76
+ scored.sort((a, b) => b.similarity - a.similarity);
77
+ const candidates = scored.slice(0, maxAttempts);
78
+ // For each candidate, walk forward window to find regret signal
79
+ const pastAttempts = [];
80
+ const windowMs = windowDays * 86_400_000;
81
+ for (const { commit: attempt, similarity } of candidates) {
82
+ const tStart = new Date(attempt.authorDate).getTime();
83
+ let regret;
84
+ let kind = "none";
85
+ let daysToRegret = 0;
86
+ for (const c of sorted) {
87
+ if (c.hash === attempt.hash)
88
+ continue;
89
+ const tC = new Date(c.authorDate).getTime();
90
+ if (tC <= tStart)
91
+ continue;
92
+ if (tC - tStart > windowMs)
93
+ break;
94
+ // Must share at least one file with the attempt
95
+ if (!shareFiles(attempt, c))
96
+ continue;
97
+ const text = `${c.subject}\n${c.body || ""}`;
98
+ if (REVERT_RE.test(text)) {
99
+ regret = c;
100
+ kind = "revert";
101
+ daysToRegret = Number(((tC - tStart) / 86_400_000).toFixed(1));
102
+ break;
103
+ }
104
+ if (HOTFIX_RE.test(text)) {
105
+ regret = c;
106
+ kind = "hotfix";
107
+ daysToRegret = Number(((tC - tStart) / 86_400_000).toFixed(1));
108
+ break;
109
+ }
110
+ if (INCIDENT_RE.test(text)) {
111
+ regret = c;
112
+ kind = "incident";
113
+ daysToRegret = Number(((tC - tStart) / 86_400_000).toFixed(1));
114
+ break;
115
+ }
116
+ if (REWRITE_RE.test(text) && shareFiles(attempt, c)) {
117
+ regret = c;
118
+ kind = "rewrite";
119
+ daysToRegret = Number(((tC - tStart) / 86_400_000).toFixed(1));
120
+ break;
121
+ }
122
+ }
123
+ pastAttempts.push({ attempt, regret, riskKind: kind, daysToRegret, similarity });
124
+ }
125
+ // Compute regret probability
126
+ const regretCount = pastAttempts.filter((p) => p.riskKind !== "none").length;
127
+ const regretProbability = pastAttempts.length === 0 ? 0 : regretCount / pastAttempts.length;
128
+ // Distill top risks: cluster regrets by riskKind, label them.
129
+ const byKind = new Map();
130
+ for (const p of pastAttempts) {
131
+ if (p.riskKind === "none")
132
+ continue;
133
+ const arr = byKind.get(p.riskKind) ?? [];
134
+ arr.push(p);
135
+ byKind.set(p.riskKind, arr);
136
+ }
137
+ const topRisks = [];
138
+ for (const [kind, arr] of byKind) {
139
+ topRisks.push({
140
+ label: riskLabel(kind, arr),
141
+ evidence: arr.map((a) => a.regret).filter(Boolean),
142
+ weight: Math.min(1, arr.length / Math.max(1, pastAttempts.length)),
143
+ });
144
+ }
145
+ topRisks.sort((a, b) => b.weight - a.weight);
146
+ // Verdict
147
+ let verdict = "low";
148
+ if (regretProbability >= 0.7)
149
+ verdict = "very_high";
150
+ else if (regretProbability >= 0.4)
151
+ verdict = "high";
152
+ else if (regretProbability >= 0.15)
153
+ verdict = "medium";
154
+ const summary = composeSummary(intent, pastAttempts.length, regretCount, verdict);
155
+ return {
156
+ intent,
157
+ pastAttempts,
158
+ regretProbability,
159
+ topRisks: topRisks.slice(0, 3),
160
+ verdict,
161
+ summary,
162
+ };
163
+ }
164
+ function shareFiles(a, b) {
165
+ if (!a.files?.length || !b.files?.length)
166
+ return false;
167
+ const setA = new Set(a.files);
168
+ for (const f of b.files)
169
+ if (setA.has(f))
170
+ return true;
171
+ return false;
172
+ }
173
+ function riskLabel(kind, arr) {
174
+ const n = arr.length;
175
+ switch (kind) {
176
+ case "revert":
177
+ return `change reverted within ${Math.round(median(arr.map((a) => a.daysToRegret)))}d (${n}× before)`;
178
+ case "hotfix":
179
+ return `hotfix follow-up needed (${n}× before)`;
180
+ case "incident":
181
+ return `linked to incident/regression (${n}× before)`;
182
+ case "rewrite":
183
+ return `partially rewritten soon after (${n}× before)`;
184
+ }
185
+ }
186
+ function median(xs) {
187
+ if (xs.length === 0)
188
+ return 0;
189
+ const s = [...xs].sort((a, b) => a - b);
190
+ const m = Math.floor(s.length / 2);
191
+ return s.length % 2 === 1 ? s[m] : (s[m - 1] + s[m]) / 2;
192
+ }
193
+ function composeSummary(intent, total, regret, verdict) {
194
+ if (total === 0) {
195
+ return `No similar past attempts found in this repo for "${intent}". Premortem cannot calibrate against history — proceed with normal review.`;
196
+ }
197
+ const pct = Math.round((regret / total) * 100);
198
+ switch (verdict) {
199
+ case "very_high":
200
+ return `${regret} of ${total} similar past attempts ended badly (${pct}%). This pattern has burned this repo before — slow down, write tests first, and review the cited commits.`;
201
+ case "high":
202
+ return `${regret} of ${total} similar past attempts hit problems (${pct}%). High caution warranted; review the cited risks before starting.`;
203
+ case "medium":
204
+ return `${regret} of ${total} similar past attempts had issues (${pct}%). Some historical risk — worth reading the cited commits.`;
205
+ case "low":
206
+ return `Only ${regret} of ${total} similar past attempts had problems (${pct}%). This kind of change has gone smoothly before — usual care is fine.`;
207
+ }
208
+ }
209
+ //# sourceMappingURL=premortem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"premortem.js","sourceRoot":"","sources":["../../src/insights/premortem.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=premortem.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"premortem.test.d.ts","sourceRoot":"","sources":["../../src/insights/premortem.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,169 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildPremortem, scoreSimilarity } from "./premortem.js";
3
+ function mk(p) {
4
+ return {
5
+ shortHash: p.hash.slice(0, 7),
6
+ authorName: "Test",
7
+ authorEmail: "t@e.com",
8
+ committerDate: p.authorDate,
9
+ body: p.body ?? "",
10
+ files: p.files ?? ["src/cache.ts"],
11
+ parents: p.parents ?? [],
12
+ ...p,
13
+ };
14
+ }
15
+ describe("scoreSimilarity", () => {
16
+ it("scores 0 when no token overlap", () => {
17
+ const c = mk({ hash: "a1", authorDate: "2024-01-01", subject: "fix typo in readme" });
18
+ const s = scoreSimilarity("rebuild authentication layer", c);
19
+ expect(s).toBe(0);
20
+ });
21
+ it("scores higher when intent terms appear in commit", () => {
22
+ const c = mk({
23
+ hash: "a1",
24
+ authorDate: "2024-01-01",
25
+ subject: "add caching layer to api responses",
26
+ });
27
+ const s = scoreSimilarity("add caching layer", c);
28
+ expect(s).toBeGreaterThan(0.5);
29
+ });
30
+ it("ignores stopwords", () => {
31
+ const c = mk({ hash: "a1", authorDate: "2024-01-01", subject: "the a an of to in for" });
32
+ const s = scoreSimilarity("the a an of to", c);
33
+ expect(s).toBe(0);
34
+ });
35
+ it("boosts score when intent names a file the commit touched", () => {
36
+ const c = mk({
37
+ hash: "a1",
38
+ authorDate: "2024-01-01",
39
+ subject: "minor unrelated change",
40
+ files: ["src/auth.ts"],
41
+ });
42
+ const s = scoreSimilarity("rewrite src/auth.ts", c);
43
+ expect(s).toBeGreaterThan(0);
44
+ });
45
+ });
46
+ describe("buildPremortem", () => {
47
+ it("returns empty when no commits at all", () => {
48
+ const r = buildPremortem("add caching layer", []);
49
+ expect(r.pastAttempts).toHaveLength(0);
50
+ expect(r.regretProbability).toBe(0);
51
+ expect(r.verdict).toBe("low");
52
+ });
53
+ it("flags very_high verdict when most past attempts ended in revert", () => {
54
+ const commits = [];
55
+ // 4 attempts to add caching, 3 reverted
56
+ for (let i = 0; i < 4; i++) {
57
+ commits.push(mk({
58
+ hash: `att${i}`,
59
+ authorDate: `2024-0${i + 1}-01`,
60
+ subject: `add caching layer to api`,
61
+ files: ["src/cache.ts"],
62
+ }));
63
+ if (i < 3) {
64
+ commits.push(mk({
65
+ hash: `rev${i}`,
66
+ authorDate: `2024-0${i + 1}-05`,
67
+ subject: `revert "add caching layer to api"`,
68
+ files: ["src/cache.ts"],
69
+ }));
70
+ }
71
+ }
72
+ const r = buildPremortem("add caching layer", commits, { similarityFloor: 0.2 });
73
+ expect(r.regretProbability).toBeGreaterThanOrEqual(0.7);
74
+ expect(r.verdict).toBe("very_high");
75
+ });
76
+ it("flags low verdict when no past attempts had problems", () => {
77
+ const commits = [];
78
+ for (let i = 0; i < 3; i++) {
79
+ commits.push(mk({
80
+ hash: `att${i}`,
81
+ authorDate: `2024-0${i + 1}-01`,
82
+ subject: `add caching layer for api`,
83
+ files: ["src/cache.ts"],
84
+ }));
85
+ commits.push(mk({
86
+ hash: `success${i}`,
87
+ authorDate: `2024-0${i + 1}-05`,
88
+ subject: `extend caching to user objects`,
89
+ files: ["src/cache.ts"],
90
+ }));
91
+ }
92
+ const r = buildPremortem("add caching layer", commits, { similarityFloor: 0.2 });
93
+ expect(r.regretProbability).toBe(0);
94
+ expect(r.verdict).toBe("low");
95
+ });
96
+ it("clusters risks by kind", () => {
97
+ const commits = [
98
+ mk({ hash: "a1", authorDate: "2024-01-01", subject: "add cache" }),
99
+ mk({ hash: "a2", authorDate: "2024-01-03", subject: "revert add cache" }),
100
+ mk({ hash: "a3", authorDate: "2024-02-01", subject: "add cache for users" }),
101
+ mk({ hash: "a4", authorDate: "2024-02-05", subject: "hotfix cache invalidation" }),
102
+ mk({ hash: "a5", authorDate: "2024-03-01", subject: "add cache for orders" }),
103
+ mk({ hash: "a6", authorDate: "2024-03-04", subject: "incident: stale cache caused outage" }),
104
+ ];
105
+ const r = buildPremortem("add cache", commits, { similarityFloor: 0.2 });
106
+ const kinds = new Set(r.topRisks.map((x) => x.label.split(" ")[0]));
107
+ expect(kinds.size).toBeGreaterThan(0);
108
+ });
109
+ it("limits topRisks to at most 3", () => {
110
+ const commits = [];
111
+ const subjects = [
112
+ "add cache",
113
+ "revert add cache",
114
+ "add cache",
115
+ "hotfix add cache bug",
116
+ "add cache",
117
+ "incident outage cache",
118
+ "add cache",
119
+ "rewrite cache after issues",
120
+ ];
121
+ for (let i = 0; i < subjects.length; i++) {
122
+ commits.push(mk({ hash: `c${i}`, authorDate: `2024-${(i + 1).toString().padStart(2, "0")}-01`, subject: subjects[i] }));
123
+ }
124
+ const r = buildPremortem("add cache", commits, { similarityFloor: 0.2 });
125
+ expect(r.topRisks.length).toBeLessThanOrEqual(3);
126
+ });
127
+ it("does not count the attempt itself as its own regret", () => {
128
+ const commits = [
129
+ mk({ hash: "a1", authorDate: "2024-01-01", subject: "add caching layer" }),
130
+ ];
131
+ const r = buildPremortem("add caching layer", commits, { similarityFloor: 0.2 });
132
+ expect(r.pastAttempts[0].riskKind).toBe("none");
133
+ });
134
+ it("computes summary string for every verdict tier", () => {
135
+ const verdicts = [
136
+ "low",
137
+ "medium",
138
+ "high",
139
+ "very_high",
140
+ ];
141
+ const seen = new Set();
142
+ for (const v of verdicts) {
143
+ const commits = [];
144
+ const total = 10;
145
+ const regrets = v === "low" ? 0 : v === "medium" ? 2 : v === "high" ? 5 : 8;
146
+ for (let i = 0; i < total; i++) {
147
+ commits.push(mk({
148
+ hash: `a${i}`,
149
+ authorDate: `2024-${(i + 1).toString().padStart(2, "0")}-01`,
150
+ subject: `add caching feature`,
151
+ files: ["src/cache.ts"],
152
+ }));
153
+ if (i < regrets) {
154
+ commits.push(mk({
155
+ hash: `r${i}`,
156
+ authorDate: `2024-${(i + 1).toString().padStart(2, "0")}-05`,
157
+ subject: `revert add caching feature`,
158
+ files: ["src/cache.ts"],
159
+ }));
160
+ }
161
+ }
162
+ const r = buildPremortem("add caching feature", commits, { similarityFloor: 0.2 });
163
+ seen.add(r.verdict);
164
+ expect(r.summary.length).toBeGreaterThan(20);
165
+ }
166
+ expect(seen.size).toBeGreaterThanOrEqual(2);
167
+ });
168
+ });
169
+ //# sourceMappingURL=premortem.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"premortem.test.js","sourceRoot":"","sources":["../../src/insights/premortem.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * `mneme regret` — surface commits that we shipped and then immediately
3
+ * fixed, hotfixed, or reverted. The "things we regretted" log.
4
+ *
5
+ * Reverse-mining git history for failure patterns. A commit is a "regret"
6
+ * if a follow-up commit (within `windowDays`) contains revert/hotfix/fix
7
+ * markers OR touches the same lines/files. The follow-up tells us this
8
+ * commit ALREADY caused pain.
9
+ *
10
+ * Why this is useful:
11
+ * - Postmortems: "what did we ship and regret?"
12
+ * - Retrospectives: pattern of regret reveals systemic issues
13
+ * - Pre-commit awareness: see what *kinds* of changes you tend to redo
14
+ *
15
+ * Pure data extraction — no LLM. The CLI command renders.
16
+ */
17
+ import type { Commit } from "../types.js";
18
+ export interface Regret {
19
+ shipped: Commit;
20
+ followup: Commit;
21
+ /** Days between ship and followup. */
22
+ daysToFix: number;
23
+ /**
24
+ * What kind of follow-up — drives the rendered label and lesson tone.
25
+ * 'revert' is strongest (full undo); 'hotfix' is urgent fix; 'fix' is
26
+ * any subsequent commit referencing the original; 'sameFiles' is the
27
+ * weakest signal (just touched the same files within window).
28
+ */
29
+ kind: "revert" | "hotfix" | "fix" | "sameFiles";
30
+ /** A 1-line lesson when one is extractable from the follow-up message. */
31
+ lesson?: string;
32
+ }
33
+ /**
34
+ * Detect regrets from a chronological commit list. Commits should be
35
+ * sorted oldest-first (caller's responsibility — we use index ordering
36
+ * to compute "follow-up").
37
+ */
38
+ export declare function detectRegrets(commits: Commit[], opts?: {
39
+ windowDays?: number;
40
+ minDaysToFix?: number;
41
+ }): Regret[];
42
+ /**
43
+ * Aggregate metrics — useful for repo-level trend reporting.
44
+ *
45
+ * `regretRate` is how many of the recent shipped commits later got fixed
46
+ * within the window. We exclude shipped commits whose own subject already
47
+ * looks like a fix (so we don't double-count repair work).
48
+ */
49
+ export interface RegretSummary {
50
+ totalShipped: number;
51
+ totalRegrets: number;
52
+ regretRate: number;
53
+ byKind: Record<Regret["kind"], number>;
54
+ averageDaysToFix: number;
55
+ }
56
+ export declare function summarizeRegrets(commits: Commit[], regrets: Regret[]): RegretSummary;
57
+ //# sourceMappingURL=regret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regret.d.ts","sourceRoot":"","sources":["../../src/insights/regret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,WAAW,CAAC;IAChD,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,MAAM,EAAE,CAwDV;AAkCD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IACvC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,aAAa,CAgBpF"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * `mneme regret` — surface commits that we shipped and then immediately
3
+ * fixed, hotfixed, or reverted. The "things we regretted" log.
4
+ *
5
+ * Reverse-mining git history for failure patterns. A commit is a "regret"
6
+ * if a follow-up commit (within `windowDays`) contains revert/hotfix/fix
7
+ * markers OR touches the same lines/files. The follow-up tells us this
8
+ * commit ALREADY caused pain.
9
+ *
10
+ * Why this is useful:
11
+ * - Postmortems: "what did we ship and regret?"
12
+ * - Retrospectives: pattern of regret reveals systemic issues
13
+ * - Pre-commit awareness: see what *kinds* of changes you tend to redo
14
+ *
15
+ * Pure data extraction — no LLM. The CLI command renders.
16
+ */
17
+ const REVERT_RE = /\b(revert|reverts|reverted)\b/i;
18
+ const HOTFIX_RE = /\b(hotfix|emergency|urgent|critical)\b/i;
19
+ const FIX_RE = /\b(fix(?:es|ed)?|bug|broken|crash|regression)\b/i;
20
+ const REF_RE = /\b#?(\d{2,6})\b/;
21
+ /**
22
+ * Detect regrets from a chronological commit list. Commits should be
23
+ * sorted oldest-first (caller's responsibility — we use index ordering
24
+ * to compute "follow-up").
25
+ */
26
+ export function detectRegrets(commits, opts = {}) {
27
+ const windowMs = (opts.windowDays ?? 7) * 86_400_000;
28
+ const minDaysToFix = opts.minDaysToFix ?? 0;
29
+ const sorted = [...commits].sort((a, b) => a.authorDate.localeCompare(b.authorDate));
30
+ const out = [];
31
+ // Index commits by file for fast suffix matching.
32
+ for (let i = 0; i < sorted.length; i++) {
33
+ const shipped = sorted[i];
34
+ const shippedTime = new Date(shipped.authorDate).getTime();
35
+ const shippedHashShort = (shipped.shortHash || shipped.hash.slice(0, 7)).toLowerCase();
36
+ // Walk forward looking for a follow-up within the window.
37
+ for (let j = i + 1; j < sorted.length; j++) {
38
+ const followup = sorted[j];
39
+ const followupTime = new Date(followup.authorDate).getTime();
40
+ const dt = followupTime - shippedTime;
41
+ if (dt > windowMs)
42
+ break; // window exceeded; no later commit qualifies
43
+ const days = dt / 86_400_000;
44
+ if (days < minDaysToFix)
45
+ continue;
46
+ const fuText = `${followup.subject}\n${followup.body || ""}`;
47
+ const fuTextLower = fuText.toLowerCase();
48
+ let kind = null;
49
+ // Strongest signal: explicit revert that names the original commit.
50
+ if (REVERT_RE.test(fuText) && (fuTextLower.includes(shippedHashShort) || fuTextLower.includes(shipped.subject.toLowerCase()))) {
51
+ kind = "revert";
52
+ }
53
+ else if (REVERT_RE.test(fuText) && shareFiles(shipped, followup)) {
54
+ kind = "revert";
55
+ }
56
+ else if (HOTFIX_RE.test(fuText) && shareFiles(shipped, followup)) {
57
+ kind = "hotfix";
58
+ }
59
+ else if (FIX_RE.test(fuText) && shareFiles(shipped, followup)) {
60
+ // Don't double-count the shipped commit itself if it's also a fix.
61
+ if (!FIX_RE.test(shipped.subject))
62
+ kind = "fix";
63
+ }
64
+ else if (REF_RE.test(fuText) && shareFiles(shipped, followup)) {
65
+ // weak signal: same files within window — only count if not already a fix
66
+ kind = "sameFiles";
67
+ }
68
+ if (kind) {
69
+ out.push({
70
+ shipped,
71
+ followup,
72
+ daysToFix: Number(days.toFixed(1)),
73
+ kind,
74
+ lesson: extractLesson(fuText, kind),
75
+ });
76
+ break; // one regret per shipped commit (the first follow-up)
77
+ }
78
+ }
79
+ }
80
+ // Sort by recency of ship date (newest first — what users want to see).
81
+ return out.sort((a, b) => b.shipped.authorDate.localeCompare(a.shipped.authorDate));
82
+ }
83
+ function shareFiles(a, b) {
84
+ if (!a.files?.length || !b.files?.length)
85
+ return false;
86
+ const setA = new Set(a.files);
87
+ for (const f of b.files)
88
+ if (setA.has(f))
89
+ return true;
90
+ return false;
91
+ }
92
+ function extractLesson(text, kind) {
93
+ // Simple heuristic — find the first sentence that explains WHY.
94
+ const lines = text.split(/\n+/);
95
+ for (const line of lines) {
96
+ const t = line.trim();
97
+ if (!t)
98
+ continue;
99
+ if (t.length < 20 || t.length > 200)
100
+ continue;
101
+ // Skip the subject line (usually the first non-empty)
102
+ if (t.toLowerCase().startsWith("revert "))
103
+ continue;
104
+ if (t.toLowerCase().startsWith("hotfix"))
105
+ continue;
106
+ return t;
107
+ }
108
+ // Fallback — generic lesson by kind.
109
+ switch (kind) {
110
+ case "revert":
111
+ return "this change was fully reverted within the window";
112
+ case "hotfix":
113
+ return "needed an urgent follow-up fix";
114
+ case "fix":
115
+ return "a follow-up commit fixed it";
116
+ case "sameFiles":
117
+ return "the same files were touched again very soon after";
118
+ }
119
+ }
120
+ export function summarizeRegrets(commits, regrets) {
121
+ const shippedCandidates = commits.filter((c) => !FIX_RE.test(c.subject));
122
+ const total = shippedCandidates.length;
123
+ const byKind = { revert: 0, hotfix: 0, fix: 0, sameFiles: 0 };
124
+ let sumDays = 0;
125
+ for (const r of regrets) {
126
+ byKind[r.kind] += 1;
127
+ sumDays += r.daysToFix;
128
+ }
129
+ return {
130
+ totalShipped: total,
131
+ totalRegrets: regrets.length,
132
+ regretRate: total === 0 ? 0 : regrets.length / total,
133
+ byKind,
134
+ averageDaysToFix: regrets.length === 0 ? 0 : sumDays / regrets.length,
135
+ };
136
+ }
137
+ //# sourceMappingURL=regret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regret.js","sourceRoot":"","sources":["../../src/insights/regret.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=regret.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regret.test.d.ts","sourceRoot":"","sources":["../../src/insights/regret.test.ts"],"names":[],"mappings":""}