@mneme-ai/core 0.8.4 → 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 (272) hide show
  1. package/dist/entities/go-parser.d.ts +47 -0
  2. package/dist/entities/go-parser.d.ts.map +1 -0
  3. package/dist/entities/go-parser.js +315 -0
  4. package/dist/entities/go-parser.js.map +1 -0
  5. package/dist/entities/go-parser.test.d.ts +2 -0
  6. package/dist/entities/go-parser.test.d.ts.map +1 -0
  7. package/dist/entities/go-parser.test.js +147 -0
  8. package/dist/entities/go-parser.test.js.map +1 -0
  9. package/dist/entities/index.d.ts +1 -0
  10. package/dist/entities/index.d.ts.map +1 -1
  11. package/dist/entities/index.js +1 -0
  12. package/dist/entities/index.js.map +1 -1
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/indexer/indexer.d.ts +12 -0
  18. package/dist/indexer/indexer.d.ts.map +1 -1
  19. package/dist/indexer/indexer.js +28 -1
  20. package/dist/indexer/indexer.js.map +1 -1
  21. package/dist/insights/bus-factor.d.ts +58 -0
  22. package/dist/insights/bus-factor.d.ts.map +1 -0
  23. package/dist/insights/bus-factor.js +117 -0
  24. package/dist/insights/bus-factor.js.map +1 -0
  25. package/dist/insights/bus-factor.test.d.ts +2 -0
  26. package/dist/insights/bus-factor.test.d.ts.map +1 -0
  27. package/dist/insights/bus-factor.test.js +149 -0
  28. package/dist/insights/bus-factor.test.js.map +1 -0
  29. package/dist/insights/commit-coach.d.ts +80 -0
  30. package/dist/insights/commit-coach.d.ts.map +1 -0
  31. package/dist/insights/commit-coach.js +230 -0
  32. package/dist/insights/commit-coach.js.map +1 -0
  33. package/dist/insights/commit-coach.test.d.ts +2 -0
  34. package/dist/insights/commit-coach.test.d.ts.map +1 -0
  35. package/dist/insights/commit-coach.test.js +163 -0
  36. package/dist/insights/commit-coach.test.js.map +1 -0
  37. package/dist/insights/crystal-ball.d.ts +76 -0
  38. package/dist/insights/crystal-ball.d.ts.map +1 -0
  39. package/dist/insights/crystal-ball.js +219 -0
  40. package/dist/insights/crystal-ball.js.map +1 -0
  41. package/dist/insights/crystal-ball.test.d.ts +2 -0
  42. package/dist/insights/crystal-ball.test.d.ts.map +1 -0
  43. package/dist/insights/crystal-ball.test.js +157 -0
  44. package/dist/insights/crystal-ball.test.js.map +1 -0
  45. package/dist/insights/decisions.d.ts +38 -0
  46. package/dist/insights/decisions.d.ts.map +1 -0
  47. package/dist/insights/decisions.js +125 -0
  48. package/dist/insights/decisions.js.map +1 -0
  49. package/dist/insights/decisions.test.d.ts +2 -0
  50. package/dist/insights/decisions.test.d.ts.map +1 -0
  51. package/dist/insights/decisions.test.js +141 -0
  52. package/dist/insights/decisions.test.js.map +1 -0
  53. package/dist/insights/dream.d.ts +71 -0
  54. package/dist/insights/dream.d.ts.map +1 -0
  55. package/dist/insights/dream.js +235 -0
  56. package/dist/insights/dream.js.map +1 -0
  57. package/dist/insights/dream.test.d.ts +2 -0
  58. package/dist/insights/dream.test.d.ts.map +1 -0
  59. package/dist/insights/dream.test.js +127 -0
  60. package/dist/insights/dream.test.js.map +1 -0
  61. package/dist/insights/index.d.ts +21 -0
  62. package/dist/insights/index.d.ts.map +1 -0
  63. package/dist/insights/index.js +21 -0
  64. package/dist/insights/index.js.map +1 -0
  65. package/dist/insights/obsidian.d.ts +42 -0
  66. package/dist/insights/obsidian.d.ts.map +1 -0
  67. package/dist/insights/obsidian.js +263 -0
  68. package/dist/insights/obsidian.js.map +1 -0
  69. package/dist/insights/obsidian.test.d.ts +2 -0
  70. package/dist/insights/obsidian.test.d.ts.map +1 -0
  71. package/dist/insights/obsidian.test.js +241 -0
  72. package/dist/insights/obsidian.test.js.map +1 -0
  73. package/dist/insights/paradox.d.ts +36 -0
  74. package/dist/insights/paradox.d.ts.map +1 -0
  75. package/dist/insights/paradox.js +201 -0
  76. package/dist/insights/paradox.js.map +1 -0
  77. package/dist/insights/paradox.test.d.ts +2 -0
  78. package/dist/insights/paradox.test.d.ts.map +1 -0
  79. package/dist/insights/paradox.test.js +88 -0
  80. package/dist/insights/paradox.test.js.map +1 -0
  81. package/dist/insights/regret.d.ts +57 -0
  82. package/dist/insights/regret.d.ts.map +1 -0
  83. package/dist/insights/regret.js +137 -0
  84. package/dist/insights/regret.js.map +1 -0
  85. package/dist/insights/regret.test.d.ts +2 -0
  86. package/dist/insights/regret.test.d.ts.map +1 -0
  87. package/dist/insights/regret.test.js +153 -0
  88. package/dist/insights/regret.test.js.map +1 -0
  89. package/dist/insights/stack-trace.d.ts +40 -0
  90. package/dist/insights/stack-trace.d.ts.map +1 -0
  91. package/dist/insights/stack-trace.js +127 -0
  92. package/dist/insights/stack-trace.js.map +1 -0
  93. package/dist/insights/stack-trace.test.d.ts +2 -0
  94. package/dist/insights/stack-trace.test.d.ts.map +1 -0
  95. package/dist/insights/stack-trace.test.js +103 -0
  96. package/dist/insights/stack-trace.test.js.map +1 -0
  97. package/dist/insights/story.d.ts +34 -0
  98. package/dist/insights/story.d.ts.map +1 -0
  99. package/dist/insights/story.js +100 -0
  100. package/dist/insights/story.js.map +1 -0
  101. package/dist/insights/story.test.d.ts +2 -0
  102. package/dist/insights/story.test.d.ts.map +1 -0
  103. package/dist/insights/story.test.js +99 -0
  104. package/dist/insights/story.test.js.map +1 -0
  105. package/dist/insights/suggest.d.ts +29 -0
  106. package/dist/insights/suggest.d.ts.map +1 -0
  107. package/dist/insights/suggest.js +93 -0
  108. package/dist/insights/suggest.js.map +1 -0
  109. package/dist/insights/suggest.test.d.ts +2 -0
  110. package/dist/insights/suggest.test.d.ts.map +1 -0
  111. package/dist/insights/suggest.test.js +71 -0
  112. package/dist/insights/suggest.test.js.map +1 -0
  113. package/dist/insights/who-knows.d.ts +66 -0
  114. package/dist/insights/who-knows.d.ts.map +1 -0
  115. package/dist/insights/who-knows.js +125 -0
  116. package/dist/insights/who-knows.js.map +1 -0
  117. package/dist/insights/who-knows.test.d.ts +2 -0
  118. package/dist/insights/who-knows.test.d.ts.map +1 -0
  119. package/dist/insights/who-knows.test.js +109 -0
  120. package/dist/insights/who-knows.test.js.map +1 -0
  121. package/dist/quant/alpha.d.ts +87 -0
  122. package/dist/quant/alpha.d.ts.map +1 -0
  123. package/dist/quant/alpha.js +103 -0
  124. package/dist/quant/alpha.js.map +1 -0
  125. package/dist/quant/alpha.test.d.ts +2 -0
  126. package/dist/quant/alpha.test.d.ts.map +1 -0
  127. package/dist/quant/alpha.test.js +147 -0
  128. package/dist/quant/alpha.test.js.map +1 -0
  129. package/dist/quant/backtest.d.ts +57 -0
  130. package/dist/quant/backtest.d.ts.map +1 -0
  131. package/dist/quant/backtest.js +90 -0
  132. package/dist/quant/backtest.js.map +1 -0
  133. package/dist/quant/backtest.test.d.ts +2 -0
  134. package/dist/quant/backtest.test.d.ts.map +1 -0
  135. package/dist/quant/backtest.test.js +133 -0
  136. package/dist/quant/backtest.test.js.map +1 -0
  137. package/dist/quant/black-swan.d.ts +45 -0
  138. package/dist/quant/black-swan.d.ts.map +1 -0
  139. package/dist/quant/black-swan.js +112 -0
  140. package/dist/quant/black-swan.js.map +1 -0
  141. package/dist/quant/black-swan.test.d.ts +2 -0
  142. package/dist/quant/black-swan.test.d.ts.map +1 -0
  143. package/dist/quant/black-swan.test.js +131 -0
  144. package/dist/quant/black-swan.test.js.map +1 -0
  145. package/dist/quant/correlation-matrix.d.ts +54 -0
  146. package/dist/quant/correlation-matrix.d.ts.map +1 -0
  147. package/dist/quant/correlation-matrix.js +103 -0
  148. package/dist/quant/correlation-matrix.js.map +1 -0
  149. package/dist/quant/correlation-matrix.test.d.ts +2 -0
  150. package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
  151. package/dist/quant/correlation-matrix.test.js +118 -0
  152. package/dist/quant/correlation-matrix.test.js.map +1 -0
  153. package/dist/quant/drawdown.d.ts +51 -0
  154. package/dist/quant/drawdown.d.ts.map +1 -0
  155. package/dist/quant/drawdown.js +96 -0
  156. package/dist/quant/drawdown.js.map +1 -0
  157. package/dist/quant/drawdown.test.d.ts +2 -0
  158. package/dist/quant/drawdown.test.d.ts.map +1 -0
  159. package/dist/quant/drawdown.test.js +166 -0
  160. package/dist/quant/drawdown.test.js.map +1 -0
  161. package/dist/quant/greek.d.ts +55 -0
  162. package/dist/quant/greek.d.ts.map +1 -0
  163. package/dist/quant/greek.js +157 -0
  164. package/dist/quant/greek.js.map +1 -0
  165. package/dist/quant/greek.test.d.ts +2 -0
  166. package/dist/quant/greek.test.d.ts.map +1 -0
  167. package/dist/quant/greek.test.js +138 -0
  168. package/dist/quant/greek.test.js.map +1 -0
  169. package/dist/quant/implied-volatility.d.ts +65 -0
  170. package/dist/quant/implied-volatility.d.ts.map +1 -0
  171. package/dist/quant/implied-volatility.js +149 -0
  172. package/dist/quant/implied-volatility.js.map +1 -0
  173. package/dist/quant/implied-volatility.test.d.ts +2 -0
  174. package/dist/quant/implied-volatility.test.d.ts.map +1 -0
  175. package/dist/quant/implied-volatility.test.js +127 -0
  176. package/dist/quant/implied-volatility.test.js.map +1 -0
  177. package/dist/quant/index.d.ts +28 -0
  178. package/dist/quant/index.d.ts.map +1 -0
  179. package/dist/quant/index.js +28 -0
  180. package/dist/quant/index.js.map +1 -0
  181. package/dist/quant/insider-trading.d.ts +56 -0
  182. package/dist/quant/insider-trading.d.ts.map +1 -0
  183. package/dist/quant/insider-trading.js +129 -0
  184. package/dist/quant/insider-trading.js.map +1 -0
  185. package/dist/quant/insider-trading.test.d.ts +2 -0
  186. package/dist/quant/insider-trading.test.d.ts.map +1 -0
  187. package/dist/quant/insider-trading.test.js +130 -0
  188. package/dist/quant/insider-trading.test.js.map +1 -0
  189. package/dist/quant/moneyball.d.ts +48 -0
  190. package/dist/quant/moneyball.d.ts.map +1 -0
  191. package/dist/quant/moneyball.js +110 -0
  192. package/dist/quant/moneyball.js.map +1 -0
  193. package/dist/quant/moneyball.test.d.ts +2 -0
  194. package/dist/quant/moneyball.test.d.ts.map +1 -0
  195. package/dist/quant/moneyball.test.js +137 -0
  196. package/dist/quant/moneyball.test.js.map +1 -0
  197. package/dist/quant/tax-loss-harvest.d.ts +59 -0
  198. package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
  199. package/dist/quant/tax-loss-harvest.js +126 -0
  200. package/dist/quant/tax-loss-harvest.js.map +1 -0
  201. package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
  202. package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
  203. package/dist/quant/tax-loss-harvest.test.js +126 -0
  204. package/dist/quant/tax-loss-harvest.test.js.map +1 -0
  205. package/dist/retrieve/index.d.ts +2 -0
  206. package/dist/retrieve/index.d.ts.map +1 -1
  207. package/dist/retrieve/index.js +2 -0
  208. package/dist/retrieve/index.js.map +1 -1
  209. package/dist/retrieve/intent.d.ts +32 -0
  210. package/dist/retrieve/intent.d.ts.map +1 -0
  211. package/dist/retrieve/intent.js +104 -0
  212. package/dist/retrieve/intent.js.map +1 -0
  213. package/dist/retrieve/intent.test.d.ts +2 -0
  214. package/dist/retrieve/intent.test.d.ts.map +1 -0
  215. package/dist/retrieve/intent.test.js +106 -0
  216. package/dist/retrieve/intent.test.js.map +1 -0
  217. package/dist/retrieve/search.d.ts +30 -0
  218. package/dist/retrieve/search.d.ts.map +1 -1
  219. package/dist/retrieve/search.js +48 -0
  220. package/dist/retrieve/search.js.map +1 -1
  221. package/dist/retrieve/search.test.js +84 -1
  222. package/dist/retrieve/search.test.js.map +1 -1
  223. package/dist/retrieve/synthesize.d.ts +57 -0
  224. package/dist/retrieve/synthesize.d.ts.map +1 -0
  225. package/dist/retrieve/synthesize.js +191 -0
  226. package/dist/retrieve/synthesize.js.map +1 -0
  227. package/dist/retrieve/synthesize.test.d.ts +2 -0
  228. package/dist/retrieve/synthesize.test.d.ts.map +1 -0
  229. package/dist/retrieve/synthesize.test.js +127 -0
  230. package/dist/retrieve/synthesize.test.js.map +1 -0
  231. package/dist/store/schema.d.ts +2 -2
  232. package/dist/store/schema.d.ts.map +1 -1
  233. package/dist/store/schema.js +60 -2
  234. package/dist/store/schema.js.map +1 -1
  235. package/dist/store/sqlite.d.ts +2 -0
  236. package/dist/store/sqlite.d.ts.map +1 -1
  237. package/dist/store/sqlite.js +24 -0
  238. package/dist/store/sqlite.js.map +1 -1
  239. package/dist/store/sqlite.test.js +1 -1
  240. package/dist/util/index.d.ts +2 -0
  241. package/dist/util/index.d.ts.map +1 -1
  242. package/dist/util/index.js +2 -1
  243. package/dist/util/index.js.map +1 -1
  244. package/dist/util/redact.d.ts +58 -0
  245. package/dist/util/redact.d.ts.map +1 -0
  246. package/dist/util/redact.js +129 -0
  247. package/dist/util/redact.js.map +1 -0
  248. package/dist/util/redact.test.d.ts +2 -0
  249. package/dist/util/redact.test.d.ts.map +1 -0
  250. package/dist/util/redact.test.js +148 -0
  251. package/dist/util/redact.test.js.map +1 -0
  252. package/dist/wisdom/calibrator.d.ts +43 -0
  253. package/dist/wisdom/calibrator.d.ts.map +1 -0
  254. package/dist/wisdom/calibrator.js +120 -0
  255. package/dist/wisdom/calibrator.js.map +1 -0
  256. package/dist/wisdom/feedback.d.ts +45 -0
  257. package/dist/wisdom/feedback.d.ts.map +1 -0
  258. package/dist/wisdom/feedback.js +116 -0
  259. package/dist/wisdom/feedback.js.map +1 -0
  260. package/dist/wisdom/index.d.ts +15 -0
  261. package/dist/wisdom/index.d.ts.map +1 -0
  262. package/dist/wisdom/index.js +15 -0
  263. package/dist/wisdom/index.js.map +1 -0
  264. package/dist/wisdom/types.d.ts +67 -0
  265. package/dist/wisdom/types.d.ts.map +1 -0
  266. package/dist/wisdom/types.js +20 -0
  267. package/dist/wisdom/types.js.map +1 -0
  268. package/dist/wisdom/wisdom.test.d.ts +2 -0
  269. package/dist/wisdom/wisdom.test.d.ts.map +1 -0
  270. package/dist/wisdom/wisdom.test.js +144 -0
  271. package/dist/wisdom/wisdom.test.js.map +1 -0
  272. package/package.json +1 -1
@@ -0,0 +1,110 @@
1
+ /**
2
+ * `mneme moneyball` — find undervalued contributors.
3
+ *
4
+ * The Billy Beane angle on engineering: in baseball, big-volume hitters
5
+ * get the contracts but on-base-percentage wins games. In codebases:
6
+ * loud LOC-per-day contributors get promoted, but the people whose
7
+ * commits *unblock everyone else* are the real value.
8
+ *
9
+ * Metric: each commit is "valuable" by how many SUBSEQUENT commits touch
10
+ * the same files. People whose commits frequently appear upstream of
11
+ * many others' work are unblocking. People with high LOC volume but few
12
+ * downstream touches are noise generators.
13
+ *
14
+ * value_score = log(downstream_commits + 1) × distinct_collaborators
15
+ * over_undervalued = value_score / commit_count (per-commit ROI)
16
+ *
17
+ * Pure analysis. No LLM.
18
+ */
19
+ export function moneyball(commits, opts = {}) {
20
+ const windowMs = (opts.downstreamWindowDays ?? 90) * 86_400_000;
21
+ const minCommits = opts.minCommits ?? 2;
22
+ const topN = opts.topN ?? 20;
23
+ const sorted = [...commits].sort((a, b) => a.authorDate.localeCompare(b.authorDate));
24
+ const byAuthor = new Map();
25
+ for (const c of sorted) {
26
+ const key = `${c.authorName}|${c.authorEmail}`;
27
+ if (!byAuthor.has(key))
28
+ byAuthor.set(key, {
29
+ authorName: c.authorName,
30
+ authorEmail: c.authorEmail,
31
+ commits: [],
32
+ downstream: 0,
33
+ collaboratorSet: new Set(),
34
+ });
35
+ byAuthor.get(key).commits.push(c);
36
+ }
37
+ // Step 2: for each commit, count downstream commits within window.
38
+ for (let i = 0; i < sorted.length; i++) {
39
+ const upstream = sorted[i];
40
+ if (!upstream.files?.length)
41
+ continue;
42
+ const upstreamFiles = new Set(upstream.files);
43
+ const upstreamTime = new Date(upstream.authorDate).getTime();
44
+ const upstreamAuthor = `${upstream.authorName}|${upstream.authorEmail}`;
45
+ const bucket = byAuthor.get(upstreamAuthor);
46
+ for (let j = i + 1; j < sorted.length; j++) {
47
+ const later = sorted[j];
48
+ const dt = new Date(later.authorDate).getTime() - upstreamTime;
49
+ if (dt > windowMs)
50
+ break;
51
+ if (!later.files?.some((f) => upstreamFiles.has(f)))
52
+ continue;
53
+ // Count this as a downstream touch on the upstream commit.
54
+ bucket.downstream += 1;
55
+ const laterAuthor = `${later.authorName}|${later.authorEmail}`;
56
+ if (laterAuthor !== upstreamAuthor) {
57
+ bucket.collaboratorSet.add(later.authorName);
58
+ }
59
+ }
60
+ }
61
+ // Step 3: compute scores + tiers.
62
+ const scores = [];
63
+ for (const b of byAuthor.values()) {
64
+ if (b.commits.length < minCommits)
65
+ continue;
66
+ const valueScore = Math.log2(b.downstream + 1) * b.collaboratorSet.size;
67
+ const perCommitROI = b.commits.length === 0 ? 0 : valueScore / b.commits.length;
68
+ const tier = classifyMoneyballTier(b.commits.length, valueScore, perCommitROI);
69
+ scores.push({
70
+ authorName: b.authorName,
71
+ authorEmail: b.authorEmail,
72
+ commitCount: b.commits.length,
73
+ downstreamReach: b.downstream,
74
+ collaborators: b.collaboratorSet.size,
75
+ valueScore,
76
+ perCommitROI,
77
+ tier,
78
+ interpretation: buildInterpretation(tier, b.commits.length, b.downstream, b.collaboratorSet.size),
79
+ });
80
+ }
81
+ // Sort by perCommitROI desc — undervalued contributors rise.
82
+ scores.sort((a, b) => b.perCommitROI - a.perCommitROI);
83
+ return scores.slice(0, topN);
84
+ }
85
+ export function classifyMoneyballTier(commitCount, valueScore, perCommitROI) {
86
+ // Moneyball: high ROI per commit AND modest commit count (low volume, high impact).
87
+ if (perCommitROI >= 1.5 && commitCount < 30)
88
+ return "moneyball";
89
+ // Balanced: high overall value but proportional commit count.
90
+ if (valueScore >= 5 && perCommitROI >= 0.5)
91
+ return "balanced";
92
+ // Loud: lots of commits, low ROI per commit (noise).
93
+ if (commitCount >= 30 && perCommitROI < 0.3)
94
+ return "loud";
95
+ // Passive: few commits, low downstream activity.
96
+ return "passive";
97
+ }
98
+ function buildInterpretation(tier, commitCount, downstream, collaborators) {
99
+ switch (tier) {
100
+ case "moneyball":
101
+ return `Undervalued — ${commitCount} commits unblocked ${collaborators} other contributors (${downstream} downstream touches). High ROI per commit.`;
102
+ case "balanced":
103
+ return `Solid contributor — ${commitCount} commits with ${downstream} downstream effects across ${collaborators} collaborators.`;
104
+ case "loud":
105
+ return `High volume, low impact — ${commitCount} commits but only ${downstream} downstream touches. Noise > signal.`;
106
+ case "passive":
107
+ return `Limited footprint — ${commitCount} commits, ${downstream} downstream effects. Either too new or working in isolation.`;
108
+ }
109
+ }
110
+ //# sourceMappingURL=moneyball.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moneyball.js","sourceRoot":"","sources":["../../src/quant/moneyball.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=moneyball.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moneyball.test.d.ts","sourceRoot":"","sources":["../../src/quant/moneyball.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { moneyball, classifyMoneyballTier } from "./moneyball.js";
3
+ const cmt = (hash, author, date, files) => ({
4
+ hash,
5
+ shortHash: hash.slice(0, 7),
6
+ authorName: author,
7
+ authorEmail: `${author}@x`,
8
+ authorDate: `${date}T00:00:00Z`,
9
+ committerDate: `${date}T00:00:00Z`,
10
+ subject: "commit",
11
+ body: "",
12
+ parents: [],
13
+ files,
14
+ });
15
+ describe("classifyMoneyballTier", () => {
16
+ it("'moneyball' when perCommitROI ≥ 1.5 + commit count < 30", () => {
17
+ expect(classifyMoneyballTier(10, 20, 2)).toBe("moneyball");
18
+ });
19
+ it("'balanced' when value ≥ 5 + ROI ≥ 0.5", () => {
20
+ expect(classifyMoneyballTier(20, 10, 0.5)).toBe("balanced");
21
+ });
22
+ it("'loud' when ≥30 commits and ROI < 0.3", () => {
23
+ expect(classifyMoneyballTier(50, 10, 0.2)).toBe("loud");
24
+ });
25
+ it("'passive' when nothing else applies", () => {
26
+ expect(classifyMoneyballTier(5, 1, 0.2)).toBe("passive");
27
+ });
28
+ });
29
+ describe("moneyball — basic invariants", () => {
30
+ it("returns empty for empty input", () => {
31
+ expect(moneyball([])).toEqual([]);
32
+ });
33
+ it("respects minCommits filter (default 2)", () => {
34
+ const commits = [cmt("a1", "alice", "2024-08-01", ["x.ts"])];
35
+ expect(moneyball(commits)).toEqual([]); // only 1 commit
36
+ expect(moneyball(commits, { minCommits: 1 })).toHaveLength(1);
37
+ });
38
+ it("downstream reach counts later commits touching same files", () => {
39
+ const commits = [
40
+ cmt("a1", "alice", "2024-08-01", ["src/x.ts"]),
41
+ cmt("a2", "alice", "2024-08-02", ["src/x.ts"]),
42
+ cmt("b1", "bob", "2024-08-03", ["src/x.ts"]),
43
+ cmt("c1", "carol", "2024-08-04", ["src/x.ts"]),
44
+ ];
45
+ const scores = moneyball(commits, { minCommits: 1 });
46
+ const alice = scores.find((s) => s.authorName === "alice");
47
+ expect(alice.downstreamReach).toBeGreaterThan(0);
48
+ expect(alice.collaborators).toBeGreaterThanOrEqual(2); // bob + carol
49
+ });
50
+ it("ignores downstream commits beyond the time window", () => {
51
+ const commits = [
52
+ cmt("a1", "alice", "2024-08-01", ["x.ts"]),
53
+ cmt("a2", "alice", "2024-08-02", ["x.ts"]),
54
+ cmt("b1", "bob", "2026-01-01", ["x.ts"]), // 1.5 years later
55
+ ];
56
+ const scores = moneyball(commits, { minCommits: 1, downstreamWindowDays: 90 });
57
+ const alice = scores.find((s) => s.authorName === "alice");
58
+ // bob is outside 90-day window — not counted as collaborator
59
+ expect(alice.collaborators).toBe(0);
60
+ });
61
+ });
62
+ describe("moneyball — per-commit ROI sort + tier assignment", () => {
63
+ it("'moneyball' tier emerges for low-volume + high-impact authors", () => {
64
+ // alice: 3 commits, each unblocks 5 collaborators
65
+ const commits = [
66
+ cmt("a1", "alice", "2024-08-01", ["src/core.ts"]),
67
+ cmt("a2", "alice", "2024-08-02", ["src/core.ts"]),
68
+ cmt("a3", "alice", "2024-08-03", ["src/core.ts"]),
69
+ ];
70
+ // 5 collaborators each making 3 follow-up commits
71
+ const followups = ["bob", "carol", "dave", "eve", "frank"];
72
+ for (const name of followups) {
73
+ for (let j = 0; j < 3; j++) {
74
+ commits.push(cmt(`${name[0]}${j}`.padEnd(7, "x"), name, `2024-08-${String(j + 5 + followups.indexOf(name) * 2).padStart(2, "0")}`, ["src/core.ts"]));
75
+ }
76
+ }
77
+ const scores = moneyball(commits, { minCommits: 1 });
78
+ const alice = scores.find((s) => s.authorName === "alice");
79
+ expect(alice.tier).toBe("moneyball");
80
+ });
81
+ it("'loud' tier for high commit count but low downstream effect", () => {
82
+ const commits = [];
83
+ // alice: 50 commits all on isolated files (no follow-ups)
84
+ for (let i = 0; i < 50; i++) {
85
+ const day = String(i + 1).padStart(2, "0");
86
+ const month = i < 28 ? "08" : "09";
87
+ const dayInMonth = i < 28 ? day : String(i - 27).padStart(2, "0");
88
+ commits.push(cmt(`a${i}`.padEnd(7, "x"), "alice", `2024-${month}-${dayInMonth}`, [`src/iso${i}.ts`]));
89
+ }
90
+ const scores = moneyball(commits, { minCommits: 1 });
91
+ const alice = scores.find((s) => s.authorName === "alice");
92
+ expect(alice.tier).toBe("loud");
93
+ expect(alice.perCommitROI).toBeLessThan(0.3);
94
+ });
95
+ it("sorts by perCommitROI desc — moneyball candidates first", () => {
96
+ const commits = [];
97
+ // bob: 50 commits on solo files (loud)
98
+ for (let i = 0; i < 50; i++) {
99
+ const day = String(i + 1).padStart(2, "0");
100
+ const month = i < 28 ? "08" : "09";
101
+ const dayInMonth = i < 28 ? day : String(i - 27).padStart(2, "0");
102
+ commits.push(cmt(`b${i}`.padEnd(7, "x"), "bob", `2024-${month}-${dayInMonth}`, [`src/iso${i}.ts`]));
103
+ }
104
+ // alice: 3 commits on shared file
105
+ commits.push(cmt("a1", "alice", "2024-08-01", ["src/core.ts"]));
106
+ commits.push(cmt("a2", "alice", "2024-08-02", ["src/core.ts"]));
107
+ commits.push(cmt("a3", "alice", "2024-08-03", ["src/core.ts"]));
108
+ // carol + dave + eve work downstream of alice
109
+ for (const name of ["carol", "dave", "eve", "frank", "george"]) {
110
+ const offset = ["carol", "dave", "eve", "frank", "george"].indexOf(name);
111
+ for (let j = 0; j < 4; j++) {
112
+ commits.push(cmt(`${name[0]}${j}`.padEnd(7, "x"), name, `2024-08-${String(j + 4 + offset * 4).padStart(2, "0")}`, ["src/core.ts"]));
113
+ }
114
+ }
115
+ const scores = moneyball(commits, { minCommits: 1 });
116
+ const aliceIdx = scores.findIndex((s) => s.authorName === "alice");
117
+ const bobIdx = scores.findIndex((s) => s.authorName === "bob");
118
+ expect(aliceIdx).toBeLessThan(bobIdx); // alice ranked higher (moneyball)
119
+ });
120
+ });
121
+ describe("moneyball — interpretation text", () => {
122
+ it("moneyball tier emphasizes high ROI", () => {
123
+ const commits = [
124
+ cmt("a1", "alice", "2024-08-01", ["x.ts"]),
125
+ cmt("a2", "alice", "2024-08-02", ["x.ts"]),
126
+ cmt("b1", "bob", "2024-08-03", ["x.ts"]),
127
+ cmt("c1", "carol", "2024-08-04", ["x.ts"]),
128
+ cmt("d1", "dave", "2024-08-05", ["x.ts"]),
129
+ cmt("e1", "eve", "2024-08-06", ["x.ts"]),
130
+ ];
131
+ const profile = moneyball(commits, { minCommits: 1 }).find((s) => s.authorName === "alice");
132
+ if (profile.tier === "moneyball") {
133
+ expect(profile.interpretation.toLowerCase()).toMatch(/undervalued|unblocked/);
134
+ }
135
+ });
136
+ });
137
+ //# sourceMappingURL=moneyball.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moneyball.test.js","sourceRoot":"","sources":["../../src/quant/moneyball.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `mneme tax-loss-harvest` — find dead code that can be deleted to "offset"
3
+ * technical debt.
4
+ *
5
+ * The metaphor: in finance, you sell losing positions to harvest tax losses,
6
+ * which offset gains elsewhere. In a codebase, deleting dead code reduces
7
+ * cognitive surface area, freeing up budget to absorb new debt elsewhere.
8
+ *
9
+ * Scoring per file:
10
+ * • staleness — days since last touch
11
+ * • commit count — fewer commits = less load-bearing
12
+ * • entity references — files with no entities or no inbound refs
13
+ * • risk if deleted — files with incidents historically = higher risk
14
+ *
15
+ * harvest_score = staleness × (1 / max(commits, 1)) × (1 - risk)
16
+ *
17
+ * Pure data analysis. No LLM. Output is a candidate list with risk gauges.
18
+ */
19
+ import type { MnemeStore } from "../store/sqlite.js";
20
+ export interface HarvestCandidate {
21
+ filePath: string;
22
+ /** Days since last commit. */
23
+ daysSinceTouch: number;
24
+ /** Total commits ever touching this file. */
25
+ commitCount: number;
26
+ /** Number of entities (functions/classes) parsed from this file. 0 = likely dead. */
27
+ entityCount: number;
28
+ /** Number of past incidents that mentioned this file. */
29
+ incidentCount: number;
30
+ /** Composite harvest score — higher = better deletion candidate. */
31
+ harvestScore: number;
32
+ /** Risk tier of deletion. */
33
+ risk: "safe" | "low-risk" | "moderate" | "risky";
34
+ /** A 1-line action recommendation. */
35
+ recommendation: string;
36
+ }
37
+ export interface HarvestOptions {
38
+ /** Minimum days since last touch to consider. Default 180. */
39
+ minStaleDays?: number;
40
+ /** Maximum total commits to consider. Default 5 (low-load). */
41
+ maxCommits?: number;
42
+ /** Top-N candidates. */
43
+ topN?: number;
44
+ /** Override "now" for deterministic tests. */
45
+ now?: Date;
46
+ }
47
+ export declare function findHarvestCandidates(store: MnemeStore, opts?: HarvestOptions): HarvestCandidate[];
48
+ export declare function classifyHarvestRisk(incidents: number, entities: number, commits: number): HarvestCandidate["risk"];
49
+ export interface HarvestSummary {
50
+ candidateCount: number;
51
+ /** Sum of (lines saved if all deleted) — best-effort approximation. */
52
+ estimatedLinesSaved: number;
53
+ /** Net risk-adjusted savings — savings × (1 - avg risk). */
54
+ netSavings: number;
55
+ /** A 1-line summary. */
56
+ summary: string;
57
+ }
58
+ export declare function summarizeHarvest(candidates: HarvestCandidate[]): HarvestSummary;
59
+ //# sourceMappingURL=tax-loss-harvest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax-loss-harvest.d.ts","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IACjD,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,IAAI,GAAE,cAAmB,GACxB,gBAAgB,EAAE,CAsEpB;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,gBAAgB,CAAC,MAAM,CAAC,CAK1B;AAkBD,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,gBAAgB,EAAE,GAAG,cAAc,CAe/E"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * `mneme tax-loss-harvest` — find dead code that can be deleted to "offset"
3
+ * technical debt.
4
+ *
5
+ * The metaphor: in finance, you sell losing positions to harvest tax losses,
6
+ * which offset gains elsewhere. In a codebase, deleting dead code reduces
7
+ * cognitive surface area, freeing up budget to absorb new debt elsewhere.
8
+ *
9
+ * Scoring per file:
10
+ * • staleness — days since last touch
11
+ * • commit count — fewer commits = less load-bearing
12
+ * • entity references — files with no entities or no inbound refs
13
+ * • risk if deleted — files with incidents historically = higher risk
14
+ *
15
+ * harvest_score = staleness × (1 / max(commits, 1)) × (1 - risk)
16
+ *
17
+ * Pure data analysis. No LLM. Output is a candidate list with risk gauges.
18
+ */
19
+ export function findHarvestCandidates(store, opts = {}) {
20
+ const minStale = opts.minStaleDays ?? 180;
21
+ const maxCommits = opts.maxCommits ?? 5;
22
+ const topN = opts.topN ?? 20;
23
+ const now = opts.now ?? new Date();
24
+ // 1. Per-file commit count + last touch.
25
+ const fileRows = store.db
26
+ .prepare(`SELECT
27
+ fc.path AS path,
28
+ COUNT(*) AS commits,
29
+ MAX(c.author_date) AS last_touch
30
+ FROM file_changes fc
31
+ JOIN commits c ON c.hash = fc.commit_hash
32
+ GROUP BY fc.path`)
33
+ .all();
34
+ // 2. Entity count per file.
35
+ const entityRows = store.db
36
+ .prepare(`SELECT file_path, COUNT(*) AS n FROM entities GROUP BY file_path`)
37
+ .all();
38
+ const entityCountByFile = new Map(entityRows.map((r) => [r.file_path, r.n]));
39
+ // 3. Incident-affected files (best-effort).
40
+ const incidentRows = store.db
41
+ .prepare(`SELECT affected_files FROM incidents`)
42
+ .all();
43
+ const incidentCountByFile = new Map();
44
+ for (const r of incidentRows) {
45
+ if (!r.affected_files)
46
+ continue;
47
+ let files = [];
48
+ try {
49
+ const parsed = JSON.parse(r.affected_files);
50
+ if (Array.isArray(parsed))
51
+ files = parsed.filter((x) => typeof x === "string");
52
+ }
53
+ catch {
54
+ files = r.affected_files.split(",").map((s) => s.trim()).filter(Boolean);
55
+ }
56
+ for (const f of files)
57
+ incidentCountByFile.set(f, (incidentCountByFile.get(f) ?? 0) + 1);
58
+ }
59
+ // 4. Score each candidate.
60
+ const candidates = [];
61
+ for (const r of fileRows) {
62
+ const days = (now.getTime() - new Date(r.last_touch).getTime()) / 86_400_000;
63
+ if (days < minStale)
64
+ continue;
65
+ if (r.commits > maxCommits)
66
+ continue;
67
+ const entities = entityCountByFile.get(r.path) ?? 0;
68
+ const incidents = incidentCountByFile.get(r.path) ?? 0;
69
+ // Risk: incident history makes deletion riskier.
70
+ const risk = Math.min(1, incidents / 5);
71
+ const harvestScore = (days / 365) * (1 / Math.max(r.commits, 1)) * (1 - risk);
72
+ candidates.push({
73
+ filePath: r.path,
74
+ daysSinceTouch: Math.round(days),
75
+ commitCount: r.commits,
76
+ entityCount: entities,
77
+ incidentCount: incidents,
78
+ harvestScore: Math.round(harvestScore * 1000) / 1000,
79
+ risk: classifyHarvestRisk(incidents, entities, r.commits),
80
+ recommendation: buildHarvestRecommendation(days, entities, incidents),
81
+ });
82
+ }
83
+ candidates.sort((a, b) => b.harvestScore - a.harvestScore);
84
+ return candidates.slice(0, topN);
85
+ }
86
+ export function classifyHarvestRisk(incidents, entities, commits) {
87
+ if (incidents >= 3)
88
+ return "risky";
89
+ if (incidents >= 1 || entities >= 10)
90
+ return "moderate";
91
+ if (commits >= 4)
92
+ return "low-risk";
93
+ return "safe";
94
+ }
95
+ function buildHarvestRecommendation(days, entities, incidents) {
96
+ if (incidents >= 3) {
97
+ return "Risky to delete — past incidents associated with this file. Inspect manually.";
98
+ }
99
+ if (entities === 0 && days > 365) {
100
+ return "Likely safe to delete — no entities parsed and untouched for over a year.";
101
+ }
102
+ if (entities === 0) {
103
+ return "Probably dead — no entities. Verify no runtime imports before deleting.";
104
+ }
105
+ if (days > 730) {
106
+ return "Untouched for 2+ years. Strong candidate, but check downstream usage first.";
107
+ }
108
+ return "Stale candidate. Run static analysis to confirm no inbound references.";
109
+ }
110
+ export function summarizeHarvest(candidates) {
111
+ if (candidates.length === 0) {
112
+ return { candidateCount: 0, estimatedLinesSaved: 0, netSavings: 0, summary: "No harvestable candidates." };
113
+ }
114
+ // Approximate 50 LOC per file as a rough heuristic — refine by reading file size if needed.
115
+ const linesPerFile = 50;
116
+ const estLines = candidates.length * linesPerFile;
117
+ const avgRisk = candidates.reduce((s, c) => s + (c.risk === "risky" ? 1 : c.risk === "moderate" ? 0.5 : 0.1), 0) / candidates.length;
118
+ const netSavings = Math.round(estLines * (1 - avgRisk));
119
+ return {
120
+ candidateCount: candidates.length,
121
+ estimatedLinesSaved: estLines,
122
+ netSavings,
123
+ summary: `${candidates.length} harvestable files — ~${estLines} LOC potential, ~${netSavings} net of risk.`,
124
+ };
125
+ }
126
+ //# sourceMappingURL=tax-loss-harvest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax-loss-harvest.js","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAiCH,MAAM,UAAU,qBAAqB,CACnC,KAAiB,EACjB,OAAuB,EAAE;IAEzB,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,IAAI,GAAG,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IAEnC,yCAAyC;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,EAAE;SACtB,OAAO,CACN;;;;;;wBAMkB,CACnB;SACA,GAAG,EAAkE,CAAC;IAEzE,4BAA4B;IAC5B,MAAM,UAAU,GAAG,KAAK,CAAC,EAAE;SACxB,OAAO,CAAC,kEAAkE,CAAC;SAC3E,GAAG,EAA6C,CAAC;IACpD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7E,4CAA4C;IAC5C,MAAM,YAAY,GAAG,KAAK,CAAC,EAAE;SAC1B,OAAO,CAAC,sCAAsC,CAAC;SAC/C,GAAG,EAA8C,CAAC;IACrD,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtD,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,CAAC,cAAc;YAAE,SAAS;QAChC,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;YAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;QAC9F,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,GAAG,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3E,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,mBAAmB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3F,CAAC;IAED,2BAA2B;IAC3B,MAAM,UAAU,GAAuB,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,UAAU,CAAC;QAC7E,IAAI,IAAI,GAAG,QAAQ;YAAE,SAAS;QAC9B,IAAI,CAAC,CAAC,OAAO,GAAG,UAAU;YAAE,SAAS;QAErC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvD,iDAAiD;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;QACxC,MAAM,YAAY,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAE9E,UAAU,CAAC,IAAI,CAAC;YACd,QAAQ,EAAE,CAAC,CAAC,IAAI;YAChB,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAChC,WAAW,EAAE,CAAC,CAAC,OAAO;YACtB,WAAW,EAAE,QAAQ;YACrB,aAAa,EAAE,SAAS;YACxB,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,IAAI;YACpD,IAAI,EAAE,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC;YACzD,cAAc,EAAE,0BAA0B,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC;SACtE,CAAC,CAAC;IACL,CAAC;IAED,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;IAC3D,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,SAAiB,EACjB,QAAgB,EAChB,OAAe;IAEf,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IACnC,IAAI,SAAS,IAAI,CAAC,IAAI,QAAQ,IAAI,EAAE;QAAE,OAAO,UAAU,CAAC;IACxD,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,UAAU,CAAC;IACpC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,0BAA0B,CAAC,IAAY,EAAE,QAAgB,EAAE,SAAiB;IACnF,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACnB,OAAO,+EAA+E,CAAC;IACzF,CAAC;IACD,IAAI,QAAQ,KAAK,CAAC,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC;QACjC,OAAO,2EAA2E,CAAC;IACrF,CAAC;IACD,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,yEAAyE,CAAC;IACnF,CAAC;IACD,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC;QACf,OAAO,6EAA6E,CAAC;IACvF,CAAC;IACD,OAAO,wEAAwE,CAAC;AAClF,CAAC;AAYD,MAAM,UAAU,gBAAgB,CAAC,UAA8B;IAC7D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,mBAAmB,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC;IAC7G,CAAC;IACD,4FAA4F;IAC5F,MAAM,YAAY,GAAG,EAAE,CAAC;IACxB,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,GAAG,YAAY,CAAC;IAClD,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC;IACrI,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IACxD,OAAO;QACL,cAAc,EAAE,UAAU,CAAC,MAAM;QACjC,mBAAmB,EAAE,QAAQ;QAC7B,UAAU;QACV,OAAO,EAAE,GAAG,UAAU,CAAC,MAAM,yBAAyB,QAAQ,oBAAoB,UAAU,eAAe;KAC5G,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tax-loss-harvest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax-loss-harvest.test.d.ts","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
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 { findHarvestCandidates, classifyHarvestRisk, summarizeHarvest } from "./tax-loss-harvest.js";
7
+ let tmpDir;
8
+ let store;
9
+ beforeEach(() => {
10
+ tmpDir = mkdtempSync(join(tmpdir(), "mneme-tlh-"));
11
+ store = new MnemeStore(join(tmpDir, "mneme.db"));
12
+ });
13
+ afterEach(() => {
14
+ store.close();
15
+ rmSync(tmpDir, { recursive: true, force: true });
16
+ });
17
+ const cmt = (hash, date, files) => ({
18
+ hash,
19
+ shortHash: hash.slice(0, 7),
20
+ authorName: "alice",
21
+ authorEmail: "a@x",
22
+ authorDate: `${date}T00:00:00Z`,
23
+ committerDate: `${date}T00:00:00Z`,
24
+ subject: "x",
25
+ body: "",
26
+ parents: [],
27
+ files,
28
+ });
29
+ function seed(commits) {
30
+ store.upsertCommits(commits);
31
+ for (const c of commits) {
32
+ store.upsertFileChanges(c.files.map((f) => ({ commitHash: c.hash, path: f, changeKind: "M", insertions: 1, deletions: 0 })));
33
+ }
34
+ }
35
+ function seedIncident(affectedFiles) {
36
+ store.db
37
+ .prepare(`INSERT INTO incidents (id, source, title, occurred_at, severity, affected_files)
38
+ VALUES (?, 'manual', 'inc', ?, 'medium', ?)`)
39
+ .run(`inc-${Math.random()}`, "2024-01-01T00:00:00Z", JSON.stringify(affectedFiles));
40
+ }
41
+ describe("classifyHarvestRisk", () => {
42
+ it("'risky' when incidents ≥ 3", () => {
43
+ expect(classifyHarvestRisk(3, 0, 1)).toBe("risky");
44
+ });
45
+ it("'moderate' when entities ≥ 10 OR incidents ≥ 1", () => {
46
+ expect(classifyHarvestRisk(0, 12, 1)).toBe("moderate");
47
+ expect(classifyHarvestRisk(1, 0, 1)).toBe("moderate");
48
+ });
49
+ it("'low-risk' when commits ≥ 4 (some history)", () => {
50
+ expect(classifyHarvestRisk(0, 0, 4)).toBe("low-risk");
51
+ });
52
+ it("'safe' when nothing else applies", () => {
53
+ expect(classifyHarvestRisk(0, 0, 1)).toBe("safe");
54
+ });
55
+ });
56
+ describe("findHarvestCandidates — basic detection", () => {
57
+ it("returns empty when no files are stale enough", () => {
58
+ seed([cmt("a1", "2024-11-01", ["src/active.ts"])]);
59
+ expect(findHarvestCandidates(store, { now: new Date("2024-12-01"), minStaleDays: 180 })).toEqual([]);
60
+ });
61
+ it("flags a stale solo-touched file as a candidate", () => {
62
+ seed([cmt("a1", "2023-01-01", ["src/legacy.ts"])]);
63
+ const candidates = findHarvestCandidates(store, { now: new Date("2024-12-01") });
64
+ expect(candidates.find((c) => c.filePath === "src/legacy.ts")).toBeDefined();
65
+ });
66
+ it("respects maxCommits filter", () => {
67
+ const commits = Array.from({ length: 10 }, (_, i) => cmt(`a${i}`.padEnd(7, "x"), "2023-01-01", ["src/hot.ts"]));
68
+ seed(commits);
69
+ expect(findHarvestCandidates(store, { now: new Date("2024-12-01"), maxCommits: 5 }).find((c) => c.filePath === "src/hot.ts")).toBeUndefined();
70
+ });
71
+ it("higher harvest score for older + lower-commit files", () => {
72
+ seed([
73
+ cmt("a1", "2020-01-01", ["src/very-old.ts"]),
74
+ cmt("b1", "2023-06-01", ["src/recent-stale.ts"]),
75
+ ]);
76
+ const candidates = findHarvestCandidates(store, { now: new Date("2024-12-01"), minStaleDays: 90 });
77
+ expect(candidates[0].filePath).toBe("src/very-old.ts");
78
+ });
79
+ it("'risky' tier when file appears in 3+ incidents", () => {
80
+ seed([cmt("a1", "2023-01-01", ["src/payment.ts"])]);
81
+ seedIncident(["src/payment.ts"]);
82
+ seedIncident(["src/payment.ts"]);
83
+ seedIncident(["src/payment.ts"]);
84
+ const c = findHarvestCandidates(store, { now: new Date("2024-12-01") }).find((x) => x.filePath === "src/payment.ts");
85
+ expect(c.risk).toBe("risky");
86
+ expect(c.recommendation.toLowerCase()).toMatch(/risk|past incidents/);
87
+ });
88
+ });
89
+ describe("summarizeHarvest", () => {
90
+ it("returns zero metrics for empty input", () => {
91
+ const s = summarizeHarvest([]);
92
+ expect(s.candidateCount).toBe(0);
93
+ expect(s.estimatedLinesSaved).toBe(0);
94
+ expect(s.summary.toLowerCase()).toContain("no");
95
+ });
96
+ it("computes net-of-risk savings", () => {
97
+ const candidates = [
98
+ {
99
+ filePath: "src/a.ts",
100
+ daysSinceTouch: 500,
101
+ commitCount: 1,
102
+ entityCount: 0,
103
+ incidentCount: 0,
104
+ harvestScore: 1,
105
+ risk: "safe",
106
+ recommendation: "",
107
+ },
108
+ {
109
+ filePath: "src/b.ts",
110
+ daysSinceTouch: 600,
111
+ commitCount: 1,
112
+ entityCount: 0,
113
+ incidentCount: 4,
114
+ harvestScore: 0.5,
115
+ risk: "risky",
116
+ recommendation: "",
117
+ },
118
+ ];
119
+ const s = summarizeHarvest(candidates);
120
+ expect(s.candidateCount).toBe(2);
121
+ expect(s.estimatedLinesSaved).toBe(100);
122
+ // avg risk = (0.1 + 1) / 2 = 0.55 → net = 100 × (1 - 0.55) = 45
123
+ expect(s.netSavings).toBe(45);
124
+ });
125
+ });
126
+ //# sourceMappingURL=tax-loss-harvest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax-loss-harvest.test.js","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.test.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
1
  export * from "./search.js";
2
2
  export * from "./rerank.js";
3
+ export * from "./intent.js";
4
+ export * from "./synthesize.js";
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}