@mneme-ai/core 0.9.0 → 0.10.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 (153) 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/index.d.ts +5 -0
  30. package/dist/insights/index.d.ts.map +1 -1
  31. package/dist/insights/index.js +5 -0
  32. package/dist/insights/index.js.map +1 -1
  33. package/dist/insights/paradox.d.ts +36 -0
  34. package/dist/insights/paradox.d.ts.map +1 -0
  35. package/dist/insights/paradox.js +201 -0
  36. package/dist/insights/paradox.js.map +1 -0
  37. package/dist/insights/paradox.test.d.ts +2 -0
  38. package/dist/insights/paradox.test.d.ts.map +1 -0
  39. package/dist/insights/paradox.test.js +88 -0
  40. package/dist/insights/paradox.test.js.map +1 -0
  41. package/dist/insights/regret.d.ts +57 -0
  42. package/dist/insights/regret.d.ts.map +1 -0
  43. package/dist/insights/regret.js +137 -0
  44. package/dist/insights/regret.js.map +1 -0
  45. package/dist/insights/regret.test.d.ts +2 -0
  46. package/dist/insights/regret.test.d.ts.map +1 -0
  47. package/dist/insights/regret.test.js +153 -0
  48. package/dist/insights/regret.test.js.map +1 -0
  49. package/dist/insights/who-knows.d.ts +18 -0
  50. package/dist/insights/who-knows.d.ts.map +1 -1
  51. package/dist/insights/who-knows.js +29 -0
  52. package/dist/insights/who-knows.js.map +1 -1
  53. package/dist/insights/who-knows.test.js +63 -1
  54. package/dist/insights/who-knows.test.js.map +1 -1
  55. package/dist/quant/alpha.d.ts +87 -0
  56. package/dist/quant/alpha.d.ts.map +1 -0
  57. package/dist/quant/alpha.js +103 -0
  58. package/dist/quant/alpha.js.map +1 -0
  59. package/dist/quant/alpha.test.d.ts +2 -0
  60. package/dist/quant/alpha.test.d.ts.map +1 -0
  61. package/dist/quant/alpha.test.js +147 -0
  62. package/dist/quant/alpha.test.js.map +1 -0
  63. package/dist/quant/backtest.d.ts +57 -0
  64. package/dist/quant/backtest.d.ts.map +1 -0
  65. package/dist/quant/backtest.js +90 -0
  66. package/dist/quant/backtest.js.map +1 -0
  67. package/dist/quant/backtest.test.d.ts +2 -0
  68. package/dist/quant/backtest.test.d.ts.map +1 -0
  69. package/dist/quant/backtest.test.js +133 -0
  70. package/dist/quant/backtest.test.js.map +1 -0
  71. package/dist/quant/black-swan.d.ts +45 -0
  72. package/dist/quant/black-swan.d.ts.map +1 -0
  73. package/dist/quant/black-swan.js +112 -0
  74. package/dist/quant/black-swan.js.map +1 -0
  75. package/dist/quant/black-swan.test.d.ts +2 -0
  76. package/dist/quant/black-swan.test.d.ts.map +1 -0
  77. package/dist/quant/black-swan.test.js +131 -0
  78. package/dist/quant/black-swan.test.js.map +1 -0
  79. package/dist/quant/correlation-matrix.d.ts +54 -0
  80. package/dist/quant/correlation-matrix.d.ts.map +1 -0
  81. package/dist/quant/correlation-matrix.js +103 -0
  82. package/dist/quant/correlation-matrix.js.map +1 -0
  83. package/dist/quant/correlation-matrix.test.d.ts +2 -0
  84. package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
  85. package/dist/quant/correlation-matrix.test.js +118 -0
  86. package/dist/quant/correlation-matrix.test.js.map +1 -0
  87. package/dist/quant/drawdown.d.ts +51 -0
  88. package/dist/quant/drawdown.d.ts.map +1 -0
  89. package/dist/quant/drawdown.js +96 -0
  90. package/dist/quant/drawdown.js.map +1 -0
  91. package/dist/quant/drawdown.test.d.ts +2 -0
  92. package/dist/quant/drawdown.test.d.ts.map +1 -0
  93. package/dist/quant/drawdown.test.js +166 -0
  94. package/dist/quant/drawdown.test.js.map +1 -0
  95. package/dist/quant/greek.d.ts +55 -0
  96. package/dist/quant/greek.d.ts.map +1 -0
  97. package/dist/quant/greek.js +157 -0
  98. package/dist/quant/greek.js.map +1 -0
  99. package/dist/quant/greek.test.d.ts +2 -0
  100. package/dist/quant/greek.test.d.ts.map +1 -0
  101. package/dist/quant/greek.test.js +138 -0
  102. package/dist/quant/greek.test.js.map +1 -0
  103. package/dist/quant/implied-volatility.d.ts +65 -0
  104. package/dist/quant/implied-volatility.d.ts.map +1 -0
  105. package/dist/quant/implied-volatility.js +149 -0
  106. package/dist/quant/implied-volatility.js.map +1 -0
  107. package/dist/quant/implied-volatility.test.d.ts +2 -0
  108. package/dist/quant/implied-volatility.test.d.ts.map +1 -0
  109. package/dist/quant/implied-volatility.test.js +127 -0
  110. package/dist/quant/implied-volatility.test.js.map +1 -0
  111. package/dist/quant/index.d.ts +28 -0
  112. package/dist/quant/index.d.ts.map +1 -0
  113. package/dist/quant/index.js +28 -0
  114. package/dist/quant/index.js.map +1 -0
  115. package/dist/quant/insider-trading.d.ts +56 -0
  116. package/dist/quant/insider-trading.d.ts.map +1 -0
  117. package/dist/quant/insider-trading.js +129 -0
  118. package/dist/quant/insider-trading.js.map +1 -0
  119. package/dist/quant/insider-trading.test.d.ts +2 -0
  120. package/dist/quant/insider-trading.test.d.ts.map +1 -0
  121. package/dist/quant/insider-trading.test.js +130 -0
  122. package/dist/quant/insider-trading.test.js.map +1 -0
  123. package/dist/quant/moneyball.d.ts +48 -0
  124. package/dist/quant/moneyball.d.ts.map +1 -0
  125. package/dist/quant/moneyball.js +110 -0
  126. package/dist/quant/moneyball.js.map +1 -0
  127. package/dist/quant/moneyball.test.d.ts +2 -0
  128. package/dist/quant/moneyball.test.d.ts.map +1 -0
  129. package/dist/quant/moneyball.test.js +137 -0
  130. package/dist/quant/moneyball.test.js.map +1 -0
  131. package/dist/quant/tax-loss-harvest.d.ts +59 -0
  132. package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
  133. package/dist/quant/tax-loss-harvest.js +126 -0
  134. package/dist/quant/tax-loss-harvest.js.map +1 -0
  135. package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
  136. package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
  137. package/dist/quant/tax-loss-harvest.test.js +126 -0
  138. package/dist/quant/tax-loss-harvest.test.js.map +1 -0
  139. package/dist/retrieve/synthesize.d.ts.map +1 -1
  140. package/dist/retrieve/synthesize.js +56 -25
  141. package/dist/retrieve/synthesize.js.map +1 -1
  142. package/dist/retrieve/synthesize.test.js +26 -15
  143. package/dist/retrieve/synthesize.test.js.map +1 -1
  144. package/dist/store/schema.d.ts +2 -2
  145. package/dist/store/schema.d.ts.map +1 -1
  146. package/dist/store/schema.js +6 -2
  147. package/dist/store/schema.js.map +1 -1
  148. package/dist/store/sqlite.d.ts +2 -0
  149. package/dist/store/sqlite.d.ts.map +1 -1
  150. package/dist/store/sqlite.js +24 -0
  151. package/dist/store/sqlite.js.map +1 -1
  152. package/dist/store/sqlite.test.js +1 -1
  153. package/package.json +1 -1
@@ -0,0 +1,219 @@
1
+ /**
2
+ * `mneme crystal-ball` — predict the probability that the staged change
3
+ * will fail CI, BEFORE you push.
4
+ *
5
+ * Approach (Bayesian-ish over historical signals):
6
+ * 1. Compute a fingerprint of the staged diff:
7
+ * modules touched, file types, add/remove ratio, test changes Y/N.
8
+ * 2. Find historical commits with similar fingerprints.
9
+ * 3. Look at each one's fate within `windowDays`:
10
+ * - revert / hotfix / fix follow-up = "trouble"
11
+ * - else = "clean"
12
+ * 4. Output: prediction p(clean) = clean / total similar.
13
+ *
14
+ * Without integrated CI run history (which Mneme doesn't index by default),
15
+ * follow-up commits are our best proxy: a commit that needed a hotfix is
16
+ * indistinguishable from one that broke CI from the user's perspective.
17
+ *
18
+ * The output is honest about its limits: if `n` is small (< 5), we say
19
+ * "low signal" instead of pretending to be confident.
20
+ */
21
+ import { parseDiff } from "./commit-coach.js";
22
+ import { detectRegrets } from "./regret.js";
23
+ export function fingerprint(diff) {
24
+ const exts = new Set();
25
+ for (const f of diff.files) {
26
+ const m = f.match(/\.([a-z0-9]+)$/i);
27
+ if (m)
28
+ exts.add(m[1].toLowerCase());
29
+ }
30
+ const totalLines = diff.added + diff.removed;
31
+ const size = totalLines < 20 ? "tiny" : totalLines < 100 ? "small" : totalLines < 500 ? "medium" : "large";
32
+ const hasTests = diff.files.some((f) => /(\.test\.|\.spec\.|\/tests?\/|\/specs?\/)/.test(f));
33
+ return {
34
+ modules: [...new Set(diff.modules)].sort(),
35
+ extensions: [...exts].sort(),
36
+ shape: diff.shape,
37
+ size,
38
+ hasTests,
39
+ };
40
+ }
41
+ /**
42
+ * Score similarity between two fingerprints. Returns 0..1.
43
+ *
44
+ * Each dimension contributes a weight; the score is a weighted average:
45
+ * modules: 0.40 (Jaccard)
46
+ * extensions: 0.20 (Jaccard)
47
+ * shape: 0.20 (1.0 if equal, else 0.0)
48
+ * size: 0.10 (linear distance over 4 buckets)
49
+ * hasTests: 0.10 (1.0 if equal, else 0.0)
50
+ */
51
+ export function similarity(a, b) {
52
+ const jaccard = (xs, ys) => {
53
+ if (xs.length === 0 && ys.length === 0)
54
+ return 1;
55
+ const A = new Set(xs);
56
+ const B = new Set(ys);
57
+ let inter = 0;
58
+ for (const x of A)
59
+ if (B.has(x))
60
+ inter += 1;
61
+ const union = A.size + B.size - inter;
62
+ return union === 0 ? 0 : inter / union;
63
+ };
64
+ const modules = jaccard(a.modules, b.modules);
65
+ const exts = jaccard(a.extensions, b.extensions);
66
+ const shape = a.shape === b.shape ? 1 : 0;
67
+ const sizes = ["tiny", "small", "medium", "large"];
68
+ const sizeDist = Math.abs(sizes.indexOf(a.size) - sizes.indexOf(b.size));
69
+ const size = 1 - sizeDist / 3;
70
+ const tests = a.hasTests === b.hasTests ? 1 : 0;
71
+ return 0.4 * modules + 0.2 * exts + 0.2 * shape + 0.1 * size + 0.1 * tests;
72
+ }
73
+ const SIMILARITY_THRESHOLD = 0.5;
74
+ /**
75
+ * Predict CI/follow-up trouble probability for the given diff.
76
+ *
77
+ * @param store - the indexed store
78
+ * @param diffText - unified diff text (e.g. `git diff --staged` output)
79
+ * @param windowDays - how long after a past commit do we still count a
80
+ * follow-up as "trouble" (default 14)
81
+ */
82
+ export function predict(store, diffText, windowDays = 14) {
83
+ const parsed = parseDiff(diffText);
84
+ const fp = fingerprint(parsed);
85
+ // Pull all commits from the store + their files.
86
+ // Only consider recent-enough commits (skip ancient history).
87
+ const commitRows = store.db
88
+ .prepare(`SELECT * FROM commits ORDER BY author_date DESC LIMIT 1000`)
89
+ .all();
90
+ if (commitRows.length === 0) {
91
+ return {
92
+ fingerprint: fp,
93
+ similarN: 0,
94
+ cleanN: 0,
95
+ pClean: 0,
96
+ verdict: "unknown",
97
+ recommendation: "Index the repo first (mneme index) — no historical signal yet.",
98
+ };
99
+ }
100
+ const commits = [];
101
+ for (const r of commitRows) {
102
+ const hash = String(r.hash);
103
+ const files = store.db.prepare("SELECT path FROM file_changes WHERE commit_hash = ?").all(hash).map((x) => x.path);
104
+ commits.push({
105
+ hash,
106
+ shortHash: String(r.short_hash),
107
+ authorName: String(r.author_name),
108
+ authorEmail: String(r.author_email),
109
+ authorDate: String(r.author_date),
110
+ committerDate: String(r.committer_date),
111
+ subject: String(r.subject),
112
+ body: String(r.body || ""),
113
+ parents: r.parents ? String(r.parents).split(/\s+/).filter(Boolean) : [],
114
+ files,
115
+ });
116
+ }
117
+ // Compute similarity for each historical commit.
118
+ const scored = [];
119
+ for (const c of commits) {
120
+ const cFp = fingerprintFromCommit(c);
121
+ const sim = similarity(fp, cFp);
122
+ if (sim >= SIMILARITY_THRESHOLD)
123
+ scored.push({ commit: c, sim, fp: cFp });
124
+ }
125
+ scored.sort((a, b) => b.sim - a.sim);
126
+ // Top 50 most-similar commits are our base.
127
+ const similar = scored.slice(0, 50);
128
+ const similarN = similar.length;
129
+ if (similarN === 0) {
130
+ return {
131
+ fingerprint: fp,
132
+ similarN: 0,
133
+ cleanN: 0,
134
+ pClean: 0,
135
+ verdict: "unknown",
136
+ recommendation: "No similar past changes in this repo. Treat as exploratory — small commits, careful review.",
137
+ };
138
+ }
139
+ // Detect regrets ONLY among the similar commits (constrain corpus).
140
+ // Use only strong regret signals (revert/hotfix/fix) — the weaker
141
+ // 'sameFiles' kind is too noisy for failure prediction.
142
+ const similarHashes = new Set(similar.map((s) => s.commit.hash));
143
+ const allRegrets = detectRegrets(commits, { windowDays });
144
+ const troubledHashes = new Set(allRegrets
145
+ .filter((r) => r.kind !== "sameFiles" && similarHashes.has(r.shipped.hash))
146
+ .map((r) => r.shipped.hash));
147
+ const cleanN = similarN - troubledHashes.size;
148
+ const pClean = cleanN / similarN;
149
+ // Find most-similar past commit (top of scored list) and its outcome.
150
+ const top = similar[0];
151
+ const mostSimilar = {
152
+ hash: top.commit.shortHash || top.commit.hash.slice(0, 7),
153
+ subject: top.commit.subject,
154
+ date: top.commit.authorDate.slice(0, 10),
155
+ outcome: troubledHashes.has(top.commit.hash) ? "trouble" : "clean",
156
+ };
157
+ const verdict = similarN < 5
158
+ ? "unknown"
159
+ : pClean >= 0.85
160
+ ? "clear"
161
+ : pClean >= 0.6
162
+ ? "moderate"
163
+ : "risky";
164
+ const recommendation = buildRecommendation(verdict, similarN, troubledHashes.size, pClean);
165
+ return {
166
+ fingerprint: fp,
167
+ similarN,
168
+ cleanN,
169
+ pClean,
170
+ verdict,
171
+ mostSimilar,
172
+ recommendation,
173
+ };
174
+ }
175
+ function fingerprintFromCommit(c) {
176
+ // Reconstruct a parsed-diff-like view from commit metadata.
177
+ const files = c.files ?? [];
178
+ const modules = [
179
+ ...new Set(files.map((f) => {
180
+ const parts = f.split("/");
181
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : parts[0];
182
+ })),
183
+ ].sort();
184
+ const exts = new Set();
185
+ for (const f of files) {
186
+ const m = f.match(/\.([a-z0-9]+)$/i);
187
+ if (m)
188
+ exts.add(m[1].toLowerCase());
189
+ }
190
+ // Shape from subject conventions.
191
+ const subjectLower = c.subject.toLowerCase();
192
+ const shape = /^fix|^hotfix|^bug/.test(subjectLower)
193
+ ? "fix"
194
+ : /^refactor/.test(subjectLower)
195
+ ? "refactor"
196
+ : /^docs?|^chore/.test(subjectLower)
197
+ ? "docs"
198
+ : /^test/.test(subjectLower)
199
+ ? "test"
200
+ : /^perf/.test(subjectLower)
201
+ ? "perf"
202
+ : "feat";
203
+ // We don't have line counts; treat as "small" by default.
204
+ const hasTests = files.some((f) => /(\.test\.|\.spec\.|\/tests?\/)/.test(f));
205
+ return { modules, extensions: [...exts].sort(), shape, size: "small", hasTests };
206
+ }
207
+ function buildRecommendation(verdict, similarN, troubledN, pClean) {
208
+ switch (verdict) {
209
+ case "unknown":
210
+ return `Only ${similarN} similar past change${similarN === 1 ? "" : "s"} in history — too thin to predict. Run tests locally before pushing.`;
211
+ case "clear":
212
+ return `${(pClean * 100).toFixed(0)}% of ${similarN} similar past changes shipped cleanly. Looks like a routine change.`;
213
+ case "moderate":
214
+ return `${troubledN} of ${similarN} similar past changes needed a follow-up fix. Run lint + tests locally first.`;
215
+ case "risky":
216
+ return `${troubledN} of ${similarN} similar past changes needed a follow-up fix (${(pClean * 100).toFixed(0)}% clean rate). Strongly consider splitting this commit + extra review.`;
217
+ }
218
+ }
219
+ //# sourceMappingURL=crystal-ball.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crystal-ball.js","sourceRoot":"","sources":["../../src/insights/crystal-ball.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=crystal-ball.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crystal-ball.test.d.ts","sourceRoot":"","sources":["../../src/insights/crystal-ball.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,157 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { MnemeStore } from "../store/sqlite.js";
6
+ import { fingerprint, similarity, predict } from "./crystal-ball.js";
7
+ import { parseDiff } from "./commit-coach.js";
8
+ let tmpDir;
9
+ let store;
10
+ beforeEach(() => {
11
+ tmpDir = mkdtempSync(join(tmpdir(), "mneme-cb-test-"));
12
+ store = new MnemeStore(join(tmpDir, "mneme.db"));
13
+ });
14
+ afterEach(() => {
15
+ store.close();
16
+ rmSync(tmpDir, { recursive: true, force: true });
17
+ });
18
+ const cmt = (hash, author, date, subject, files) => ({
19
+ hash,
20
+ shortHash: hash.slice(0, 7),
21
+ authorName: author,
22
+ authorEmail: `${author}@x`,
23
+ authorDate: `${date}T00:00:00Z`,
24
+ committerDate: `${date}T00:00:00Z`,
25
+ subject,
26
+ body: "",
27
+ parents: [],
28
+ files,
29
+ });
30
+ function seed(commits) {
31
+ store.upsertCommits(commits);
32
+ for (const c of commits) {
33
+ store.upsertFileChanges(c.files.map((f) => ({
34
+ commitHash: c.hash,
35
+ path: f,
36
+ changeKind: "M",
37
+ insertions: 1,
38
+ deletions: 0,
39
+ })));
40
+ }
41
+ }
42
+ describe("fingerprint", () => {
43
+ it("extracts modules, extensions, shape, size, hasTests", () => {
44
+ const diff = parseDiff(`diff --git a/src/auth/jwt.ts b/src/auth/jwt.ts
45
+ +const x = 1;`);
46
+ const fp = fingerprint(diff);
47
+ expect(fp.modules).toEqual(["src/auth"]);
48
+ expect(fp.extensions).toEqual(["ts"]);
49
+ expect(fp.size).toBe("tiny");
50
+ expect(fp.hasTests).toBe(false);
51
+ });
52
+ it("flags hasTests when *.test.ts present", () => {
53
+ const diff = parseDiff(`diff --git a/src/foo.test.ts b/src/foo.test.ts
54
+ +test('x', () => {});`);
55
+ expect(fingerprint(diff).hasTests).toBe(true);
56
+ });
57
+ it("size buckets are tiny/small/medium/large by line count", () => {
58
+ const tiny = fingerprint({ files: [], modules: [], added: 5, removed: 5, shape: "feat" });
59
+ const small = fingerprint({ files: [], modules: [], added: 50, removed: 30, shape: "feat" });
60
+ const medium = fingerprint({ files: [], modules: [], added: 200, removed: 200, shape: "feat" });
61
+ const large = fingerprint({ files: [], modules: [], added: 800, removed: 100, shape: "feat" });
62
+ expect(tiny.size).toBe("tiny");
63
+ expect(small.size).toBe("small");
64
+ expect(medium.size).toBe("medium");
65
+ expect(large.size).toBe("large");
66
+ });
67
+ });
68
+ describe("similarity", () => {
69
+ const fp = (overrides = {}) => ({
70
+ modules: ["src/auth"],
71
+ extensions: ["ts"],
72
+ shape: "feat",
73
+ size: "small",
74
+ hasTests: false,
75
+ ...overrides,
76
+ });
77
+ it("identical fingerprints score 1.0", () => {
78
+ expect(similarity(fp(), fp())).toBe(1);
79
+ });
80
+ it("module overlap dominates the score (40% weight)", () => {
81
+ const a = fp({ modules: ["src/auth"] });
82
+ const b = fp({ modules: ["src/payment"] });
83
+ // No module overlap → modules contributes 0
84
+ // Same exts (1.0 × 0.2), shape (0.2), size (0.1), tests (0.1) = 0.6
85
+ expect(similarity(a, b)).toBeCloseTo(0.6, 1);
86
+ });
87
+ it("size distance reduces score linearly", () => {
88
+ const a = fp({ size: "tiny" });
89
+ const b = fp({ size: "large" });
90
+ const s = similarity(a, b);
91
+ // Other dims identical (0.9 weight), size dim 0
92
+ expect(s).toBeCloseTo(0.9, 1);
93
+ });
94
+ });
95
+ describe("predict — empty repo edge case", () => {
96
+ it("returns 'unknown' verdict when repo is empty", () => {
97
+ const p = predict(store, "diff --git a/src/x.ts b/src/x.ts\n+const x = 1;");
98
+ expect(p.verdict).toBe("unknown");
99
+ expect(p.similarN).toBe(0);
100
+ expect(p.recommendation.toLowerCase()).toMatch(/index|signal/);
101
+ });
102
+ });
103
+ describe("predict — clean prediction", () => {
104
+ it("predicts 'clear' when most similar past changes shipped cleanly", () => {
105
+ const seedCommits = Array.from({ length: 20 }, (_, i) => cmt(`a${i}1234567`, "alice", `2024-08-${String((i % 28) + 1).padStart(2, "0")}`, `feat(auth): add thing ${i}`, ["src/auth/jwt.ts"]));
106
+ seed(seedCommits);
107
+ const p = predict(store, `diff --git a/src/auth/jwt.ts b/src/auth/jwt.ts
108
+ +const x = 1;`);
109
+ expect(p.similarN).toBeGreaterThan(0);
110
+ expect(p.verdict).toBe("clear");
111
+ expect(p.pClean).toBeGreaterThan(0.8);
112
+ });
113
+ });
114
+ describe("predict — risky prediction", () => {
115
+ it("predicts 'risky' when many similar past changes needed follow-up fixes", () => {
116
+ const commits = [];
117
+ // 10 pairs of (shipped, fix-2-days-later) on src/auth
118
+ for (let i = 0; i < 10; i++) {
119
+ const day = String(i * 3 + 1).padStart(2, "0");
120
+ const fixDay = String(i * 3 + 3).padStart(2, "0");
121
+ commits.push(cmt(`a${i}_____`, "alice", `2024-08-${day}`, `feat(auth): add thing ${i}`, ["src/auth/jwt.ts"]));
122
+ commits.push(cmt(`b${i}_____`, "alice", `2024-08-${fixDay}`, `fix: thing ${i} broke`, ["src/auth/jwt.ts"]));
123
+ }
124
+ seed(commits);
125
+ const p = predict(store, `diff --git a/src/auth/jwt.ts b/src/auth/jwt.ts
126
+ +const x = 1;`);
127
+ expect(p.similarN).toBeGreaterThanOrEqual(5);
128
+ expect(["risky", "moderate"]).toContain(p.verdict);
129
+ expect(p.pClean).toBeLessThan(0.7);
130
+ });
131
+ });
132
+ describe("predict — low signal verdict", () => {
133
+ it("returns 'unknown' when similar count is below 5", () => {
134
+ seed([
135
+ cmt("a1", "alice", "2024-08-01", "feat: thing", ["src/auth/jwt.ts"]),
136
+ cmt("a2", "alice", "2024-08-02", "feat: thing 2", ["src/auth/jwt.ts"]),
137
+ ]);
138
+ const p = predict(store, `diff --git a/src/auth/jwt.ts b/src/auth/jwt.ts
139
+ +const x = 1;`);
140
+ expect(["unknown", "clear"]).toContain(p.verdict);
141
+ if (p.similarN < 5)
142
+ expect(p.verdict).toBe("unknown");
143
+ });
144
+ });
145
+ describe("predict — most-similar reference", () => {
146
+ it("returns the most-similar past commit alongside the prediction", () => {
147
+ seed([
148
+ cmt("a1xxxxxx", "alice", "2024-08-01", "feat(auth): unique subject", ["src/auth/jwt.ts"]),
149
+ ...Array.from({ length: 10 }, (_, i) => cmt(`b${i}xxxxxx`, "alice", `2024-09-${String(i + 1).padStart(2, "0")}`, `feat: misc`, ["src/other/foo.ts"])),
150
+ ]);
151
+ const p = predict(store, `diff --git a/src/auth/jwt.ts b/src/auth/jwt.ts
152
+ +const x = 1;`);
153
+ expect(p.mostSimilar?.hash).toBeDefined();
154
+ expect(["clean", "trouble"]).toContain(p.mostSimilar?.outcome);
155
+ });
156
+ });
157
+ //# sourceMappingURL=crystal-ball.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crystal-ball.test.js","sourceRoot":"","sources":["../../src/insights/crystal-ball.test.ts"],"names":[],"mappings":"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"}
@@ -13,4 +13,9 @@ export * from "./story.js";
13
13
  export * from "./dream.js";
14
14
  export * from "./suggest.js";
15
15
  export * from "./obsidian.js";
16
+ export * from "./regret.js";
17
+ export * from "./bus-factor.js";
18
+ export * from "./paradox.js";
19
+ export * from "./commit-coach.js";
20
+ export * from "./crystal-ball.js";
16
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/insights/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/insights/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
@@ -13,4 +13,9 @@ export * from "./story.js";
13
13
  export * from "./dream.js";
14
14
  export * from "./suggest.js";
15
15
  export * from "./obsidian.js";
16
+ export * from "./regret.js";
17
+ export * from "./bus-factor.js";
18
+ export * from "./paradox.js";
19
+ export * from "./commit-coach.js";
20
+ export * from "./crystal-ball.js";
16
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/insights/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/insights/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `mneme paradox` — find architectural flip-flops in commit history.
3
+ *
4
+ * A "flip-flop" is a chain of decisions on the same topic where the
5
+ * direction reverses: A → B → A. This usually means the team forgot
6
+ * (or never wrote down) WHY they made the original decision.
7
+ *
8
+ * Implementation: walk decisions chronologically, group by topic
9
+ * (heuristically clustered by key terms), and detect ABA-style
10
+ * chains. Conservative — only emit when confidence is high that
11
+ * the decisions are actually about the same thing.
12
+ *
13
+ * Pure data analysis. No LLM. The CLI renders.
14
+ */
15
+ import type { ExtractedDecision } from "./decisions.js";
16
+ export interface FlipFlop {
17
+ /** Topic label — extracted from the shared keyword(s). */
18
+ topic: string;
19
+ /** The chain of decisions, oldest first. */
20
+ chain: ExtractedDecision[];
21
+ /** How many direction reversals (0 = no flip, 1 = one flip, 2 = ABA). */
22
+ flips: number;
23
+ /** Months between first and last decision in the chain. */
24
+ spanMonths: number;
25
+ /** A 1-line interpretation. */
26
+ question: string;
27
+ }
28
+ /**
29
+ * Detect flip-flops across a list of decisions.
30
+ *
31
+ * Returns groups with ≥ 1 flip — the actual paradoxes worth investigating.
32
+ * Groups with 2+ decisions but no flips (just continuous progress) are
33
+ * suppressed.
34
+ */
35
+ export declare function detectParadoxes(decisions: ExtractedDecision[]): FlipFlop[];
36
+ //# sourceMappingURL=paradox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paradox.d.ts","sourceRoot":"","sources":["../../src/insights/paradox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,WAAW,QAAQ;IACvB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqJD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,iBAAiB,EAAE,GAAG,QAAQ,EAAE,CA2B1E"}