@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
package/dist/index.d.ts CHANGED
@@ -9,4 +9,5 @@ export * as enrich from "./enrich/index.js";
9
9
  export * as util from "./util/index.js";
10
10
  export * as wisdom from "./wisdom/index.js";
11
11
  export * as insights from "./insights/index.js";
12
+ export * as quant from "./quant/index.js";
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -9,4 +9,5 @@ export * as enrich from "./enrich/index.js";
9
9
  export * as util from "./util/index.js";
10
10
  export * as wisdom from "./wisdom/index.js";
11
11
  export * as insights from "./insights/index.js";
12
+ export * as quant from "./quant/index.js";
12
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `mneme bus-factor` — surface single-source-of-truth knowledge holders.
3
+ *
4
+ * For each indexed file: compute author distribution. Files where one
5
+ * author owns >75% of commits AND the file is touched frequently are
6
+ * "fragile knowledge zones" — if that person leaves, the knowledge dies.
7
+ *
8
+ * This is different from `who-knows`:
9
+ * - `who-knows` → "who knows about X?" (answer + backup)
10
+ * - `bus-factor` → "what knowledge is fragile?" (per-file ownership risk)
11
+ *
12
+ * Output ranks by criticality = ownerShare × log(touchCount). High-touch
13
+ * solo-owned files are the highest risk. Concrete pairing recommendations
14
+ * are attached when a backup author exists.
15
+ */
16
+ import type { MnemeStore } from "../store/sqlite.js";
17
+ export interface FileRisk {
18
+ filePath: string;
19
+ /** Total commits that touched this file. */
20
+ totalTouches: number;
21
+ /** Top owner — name + email + share. */
22
+ topOwner: {
23
+ name: string;
24
+ email: string;
25
+ touches: number;
26
+ sharePct: number;
27
+ lastTouch: string;
28
+ };
29
+ /** Closest second-place author (for pair recommendation). Undefined if solo-only. */
30
+ backup?: {
31
+ name: string;
32
+ email: string;
33
+ touches: number;
34
+ lastTouch: string;
35
+ };
36
+ /**
37
+ * Risk tier from criticality score:
38
+ * critical: ownerShare ≥ 0.85 AND touches ≥ 10
39
+ * high: ownerShare ≥ 0.75 AND touches ≥ 5
40
+ * medium: ownerShare ≥ 0.6 AND touches ≥ 3
41
+ * low: everything else (still surfaced when solo-touched)
42
+ */
43
+ tier: "critical" | "high" | "medium" | "low";
44
+ /** Numeric criticality — for sorting. */
45
+ score: number;
46
+ /** A 1-line lesson/recommendation. */
47
+ recommendation: string;
48
+ }
49
+ export interface BusFactorOptions {
50
+ /** Skip files that match these glob-like fragments. Defaults to lockfiles, generated dirs. */
51
+ excludePatterns?: string[];
52
+ /** Minimum touches for a file to be considered. Below this we don't have enough signal. */
53
+ minTouches?: number;
54
+ /** Cap on results returned. */
55
+ topN?: number;
56
+ }
57
+ export declare function busFactor(store: MnemeStore, opts?: BusFactorOptions): FileRisk[];
58
+ //# sourceMappingURL=bus-factor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bus-factor.d.ts","sourceRoot":"","sources":["../../src/insights/bus-factor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAChG,qFAAqF;IACrF,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E;;;;;;OAMG;IACH,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAC7C,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8FAA8F;IAC9F,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAaD,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,gBAAqB,GAAG,QAAQ,EAAE,CA8EpF"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * `mneme bus-factor` — surface single-source-of-truth knowledge holders.
3
+ *
4
+ * For each indexed file: compute author distribution. Files where one
5
+ * author owns >75% of commits AND the file is touched frequently are
6
+ * "fragile knowledge zones" — if that person leaves, the knowledge dies.
7
+ *
8
+ * This is different from `who-knows`:
9
+ * - `who-knows` → "who knows about X?" (answer + backup)
10
+ * - `bus-factor` → "what knowledge is fragile?" (per-file ownership risk)
11
+ *
12
+ * Output ranks by criticality = ownerShare × log(touchCount). High-touch
13
+ * solo-owned files are the highest risk. Concrete pairing recommendations
14
+ * are attached when a backup author exists.
15
+ */
16
+ const DEFAULT_EXCLUDES = [
17
+ "package-lock.json",
18
+ "yarn.lock",
19
+ "pnpm-lock.yaml",
20
+ "node_modules/",
21
+ "dist/",
22
+ ".min.js",
23
+ ".min.css",
24
+ "/generated/",
25
+ ];
26
+ export function busFactor(store, opts = {}) {
27
+ const minTouches = opts.minTouches ?? 3;
28
+ const excludes = opts.excludePatterns ?? DEFAULT_EXCLUDES;
29
+ const topN = opts.topN ?? 20;
30
+ // Aggregate per-file author counts via JOIN of file_changes + commits.
31
+ const rows = store.db
32
+ .prepare(`SELECT
33
+ fc.path AS path,
34
+ c.author_name AS author_name,
35
+ c.author_email AS author_email,
36
+ COUNT(*) AS touches,
37
+ MAX(c.author_date) AS last_touch
38
+ FROM file_changes fc
39
+ JOIN commits c ON c.hash = fc.commit_hash
40
+ GROUP BY fc.path, c.author_name, c.author_email
41
+ ORDER BY fc.path, touches DESC`)
42
+ .all();
43
+ const byFile = new Map();
44
+ for (const r of rows) {
45
+ if (excludes.some((p) => r.path.includes(p)))
46
+ continue;
47
+ if (!byFile.has(r.path))
48
+ byFile.set(r.path, { filePath: r.path, authors: [], totalTouches: 0 });
49
+ const b = byFile.get(r.path);
50
+ b.authors.push({ name: r.author_name, email: r.author_email, touches: r.touches, lastTouch: r.last_touch });
51
+ b.totalTouches += r.touches;
52
+ }
53
+ const risks = [];
54
+ for (const b of byFile.values()) {
55
+ if (b.totalTouches < minTouches)
56
+ continue;
57
+ b.authors.sort((a, c) => c.touches - a.touches);
58
+ const top = b.authors[0];
59
+ const sharePct = Math.round((top.touches / b.totalTouches) * 100);
60
+ const share = top.touches / b.totalTouches;
61
+ const score = share * Math.log2(b.totalTouches + 1);
62
+ const { tier, recommendation } = classifyRisk(share, b.totalTouches, b.authors);
63
+ risks.push({
64
+ filePath: b.filePath,
65
+ totalTouches: b.totalTouches,
66
+ topOwner: {
67
+ name: top.name,
68
+ email: top.email,
69
+ touches: top.touches,
70
+ sharePct,
71
+ lastTouch: top.lastTouch,
72
+ },
73
+ backup: b.authors[1]
74
+ ? {
75
+ name: b.authors[1].name,
76
+ email: b.authors[1].email,
77
+ touches: b.authors[1].touches,
78
+ lastTouch: b.authors[1].lastTouch,
79
+ }
80
+ : undefined,
81
+ tier,
82
+ score,
83
+ recommendation,
84
+ });
85
+ }
86
+ risks.sort((a, b) => b.score - a.score);
87
+ return risks.slice(0, topN);
88
+ }
89
+ function classifyRisk(share, touches, authors) {
90
+ if (share >= 0.85 && touches >= 10) {
91
+ return {
92
+ tier: "critical",
93
+ recommendation: authors.length > 1
94
+ ? `Pair with ${authors[1].name} this sprint — they are the only backup signal in history.`
95
+ : "Find a pairing partner before this person leaves. No backup currently exists.",
96
+ };
97
+ }
98
+ if (share >= 0.75 && touches >= 5) {
99
+ return {
100
+ tier: "high",
101
+ recommendation: authors.length > 1
102
+ ? `Encourage ${authors[1].name} to pick up small changes here to spread the knowledge.`
103
+ : "Solo-owned. Pair-program the next change to spread context.",
104
+ };
105
+ }
106
+ if (share >= 0.6 && touches >= 3) {
107
+ return {
108
+ tier: "medium",
109
+ recommendation: "Knowledge is concentrated but not critical. Watch for owner departure.",
110
+ };
111
+ }
112
+ return {
113
+ tier: "low",
114
+ recommendation: "Knowledge is reasonably distributed.",
115
+ };
116
+ }
117
+ //# sourceMappingURL=bus-factor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bus-factor.js","sourceRoot":"","sources":["../../src/insights/bus-factor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bus-factor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bus-factor.test.d.ts","sourceRoot":"","sources":["../../src/insights/bus-factor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,149 @@
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 { busFactor } from "./bus-factor.js";
7
+ let tmpDir;
8
+ let store;
9
+ beforeEach(() => {
10
+ tmpDir = mkdtempSync(join(tmpdir(), "mneme-busfactor-test-"));
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, author, date, files) => ({
18
+ hash,
19
+ shortHash: hash.slice(0, 7),
20
+ authorName: author,
21
+ authorEmail: `${author}@x`,
22
+ authorDate: `${date}T00:00:00Z`,
23
+ committerDate: `${date}T00:00:00Z`,
24
+ subject: `commit by ${author}`,
25
+ body: "",
26
+ parents: [],
27
+ files,
28
+ });
29
+ function seed(commits) {
30
+ store.upsertCommits(commits);
31
+ for (const c of commits) {
32
+ const changes = c.files.map((f) => ({
33
+ commitHash: c.hash,
34
+ path: f,
35
+ changeKind: "M",
36
+ insertions: 1,
37
+ deletions: 0,
38
+ }));
39
+ store.upsertFileChanges(changes);
40
+ }
41
+ }
42
+ describe("busFactor — risk tier classification", () => {
43
+ it("flags CRITICAL when one author owns ≥85% AND ≥10 touches", () => {
44
+ const commits = [];
45
+ for (let i = 0; i < 9; i++)
46
+ commits.push(cmt(`a${i}1234567`, "alice", `2024-08-0${(i % 9) + 1}`, ["src/payment.ts"]));
47
+ commits.push(cmt("b0123456", "bob", "2024-09-01", ["src/payment.ts"]));
48
+ seed(commits);
49
+ const risks = busFactor(store, { minTouches: 1 });
50
+ const payment = risks.find((r) => r.filePath === "src/payment.ts");
51
+ expect(payment.tier).toBe("critical");
52
+ expect(payment.topOwner.name).toBe("alice");
53
+ expect(payment.topOwner.sharePct).toBe(90);
54
+ expect(payment.recommendation.toLowerCase()).toContain("bob");
55
+ });
56
+ it("flags HIGH when one author owns 75-85% with 5+ touches", () => {
57
+ const commits = [
58
+ cmt("a1", "alice", "2024-08-01", ["src/auth.ts"]),
59
+ cmt("a2", "alice", "2024-08-02", ["src/auth.ts"]),
60
+ cmt("a3", "alice", "2024-08-03", ["src/auth.ts"]),
61
+ cmt("a4", "alice", "2024-08-04", ["src/auth.ts"]),
62
+ cmt("a5", "alice", "2024-08-05", ["src/auth.ts"]),
63
+ cmt("a6", "alice", "2024-08-06", ["src/auth.ts"]),
64
+ cmt("b1", "bob", "2024-08-07", ["src/auth.ts"]),
65
+ cmt("b2", "bob", "2024-08-08", ["src/auth.ts"]),
66
+ ];
67
+ seed(commits);
68
+ const risks = busFactor(store, { minTouches: 1 });
69
+ const auth = risks.find((r) => r.filePath === "src/auth.ts");
70
+ expect(auth.tier).toBe("high");
71
+ expect(auth.topOwner.sharePct).toBe(75);
72
+ });
73
+ it("flags MEDIUM when share is 60-75%", () => {
74
+ const commits = [
75
+ cmt("a1", "alice", "2024-08-01", ["src/x.ts"]),
76
+ cmt("a2", "alice", "2024-08-02", ["src/x.ts"]),
77
+ cmt("a3", "alice", "2024-08-03", ["src/x.ts"]),
78
+ cmt("b1", "bob", "2024-08-04", ["src/x.ts"]),
79
+ cmt("b2", "bob", "2024-08-05", ["src/x.ts"]),
80
+ ];
81
+ seed(commits);
82
+ const risks = busFactor(store, { minTouches: 1 });
83
+ const x = risks.find((r) => r.filePath === "src/x.ts");
84
+ expect(x.tier).toBe("medium");
85
+ expect(x.topOwner.sharePct).toBe(60);
86
+ });
87
+ });
88
+ describe("busFactor — backup recommendation", () => {
89
+ it("includes the second-place author as the backup", () => {
90
+ const commits = [
91
+ ...Array.from({ length: 9 }, (_, i) => cmt(`a${i}`, "alice", `2024-08-0${(i % 9) + 1}`, ["x.ts"])),
92
+ cmt("b1", "bob", "2024-09-01", ["x.ts"]),
93
+ cmt("c1", "carol", "2024-09-02", ["x.ts"]),
94
+ ];
95
+ seed(commits);
96
+ const risks = busFactor(store, { minTouches: 1 });
97
+ const x = risks.find((r) => r.filePath === "x.ts");
98
+ expect(x.backup?.name).toBeDefined();
99
+ expect(["bob", "carol"]).toContain(x.backup?.name);
100
+ });
101
+ it("backup undefined when only one author has ever touched the file", () => {
102
+ const commits = Array.from({ length: 5 }, (_, i) => cmt(`a${i}`, "alice", `2024-08-0${i + 1}`, ["solo.ts"]));
103
+ seed(commits);
104
+ const risks = busFactor(store, { minTouches: 1 });
105
+ const solo = risks.find((r) => r.filePath === "solo.ts");
106
+ expect(solo.backup).toBeUndefined();
107
+ expect(solo.recommendation.toLowerCase()).toMatch(/no backup|spread/);
108
+ });
109
+ });
110
+ describe("busFactor — filtering and sorting", () => {
111
+ it("excludes lockfiles + generated dirs by default", () => {
112
+ const commits = [
113
+ cmt("a1", "alice", "2024-08-01", ["package-lock.json", "src/x.ts"]),
114
+ cmt("a2", "alice", "2024-08-02", ["package-lock.json", "src/x.ts"]),
115
+ cmt("a3", "alice", "2024-08-03", ["package-lock.json", "src/x.ts"]),
116
+ ];
117
+ seed(commits);
118
+ const risks = busFactor(store, { minTouches: 1 });
119
+ expect(risks.find((r) => r.filePath === "package-lock.json")).toBeUndefined();
120
+ expect(risks.find((r) => r.filePath === "src/x.ts")).toBeDefined();
121
+ });
122
+ it("respects minTouches threshold", () => {
123
+ const commits = [cmt("a1", "alice", "2024-08-01", ["lonely.ts"])];
124
+ seed(commits);
125
+ expect(busFactor(store, { minTouches: 2 })).toEqual([]);
126
+ expect(busFactor(store, { minTouches: 1 })).toHaveLength(1);
127
+ });
128
+ it("sorts by criticality score (high-touch solo > low-touch solo)", () => {
129
+ const commits = [
130
+ ...Array.from({ length: 12 }, (_, i) => cmt(`a${i}`, "alice", `2024-08-${String(i + 1).padStart(2, "0")}`, ["hot.ts"])),
131
+ ...Array.from({ length: 3 }, (_, i) => cmt(`b${i}`, "alice", `2024-09-0${i + 1}`, ["cold.ts"])),
132
+ ];
133
+ seed(commits);
134
+ const risks = busFactor(store, { minTouches: 1 });
135
+ expect(risks[0].filePath).toBe("hot.ts");
136
+ expect(risks[0].score).toBeGreaterThan(risks[1].score);
137
+ });
138
+ it("respects topN limit", () => {
139
+ const commits = [];
140
+ for (let f = 0; f < 30; f++) {
141
+ for (let i = 0; i < 3; i++) {
142
+ commits.push(cmt(`f${f}c${i}`.padEnd(7, "x"), "alice", `2024-08-${String((i % 28) + 1).padStart(2, "0")}`, [`f${f}.ts`]));
143
+ }
144
+ }
145
+ seed(commits);
146
+ expect(busFactor(store, { minTouches: 1, topN: 5 })).toHaveLength(5);
147
+ });
148
+ });
149
+ //# sourceMappingURL=bus-factor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bus-factor.test.js","sourceRoot":"","sources":["../../src/insights/bus-factor.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * `mneme commit-coach` — pre-commit assistant.
3
+ *
4
+ * Reads a unified diff (from `git diff --staged` or stdin) and produces
5
+ * a structured advice payload covering:
6
+ * 1. Suggested commit message (style-matched to repo's history).
7
+ * 2. Recommended reviewers (top experts on touched files).
8
+ * 3. Scope warning (touching too many modules → suggest splitting).
9
+ * 4. Past-regret check (this pattern caused INC-X before).
10
+ *
11
+ * Pure data analysis. The CLI optionally calls an LLM to polish the
12
+ * commit message; everything else is heuristic + store queries.
13
+ */
14
+ import type { MnemeStore } from "../store/sqlite.js";
15
+ export interface ParsedDiff {
16
+ files: string[];
17
+ /** Unique top-2-segment modules (e.g. "src/payment", "src/auth"). */
18
+ modules: string[];
19
+ /** Total lines added across all hunks. */
20
+ added: number;
21
+ /** Total lines removed across all hunks. */
22
+ removed: number;
23
+ /** Type guess based on file paths + line counts. */
24
+ shape: "feat" | "fix" | "refactor" | "test" | "docs" | "chore" | "perf";
25
+ }
26
+ export interface Reviewer {
27
+ name: string;
28
+ email: string;
29
+ /** % of commits on the touched files attributable to this person. */
30
+ ownership: number;
31
+ /** Files this reviewer is the primary owner of (within touched set). */
32
+ ownedFiles: string[];
33
+ }
34
+ export interface RegretWarning {
35
+ pattern: string;
36
+ pastCommitHash: string;
37
+ pastDate: string;
38
+ outcome: string;
39
+ }
40
+ export interface CoachAdvice {
41
+ /** What the diff looks like. */
42
+ diff: ParsedDiff;
43
+ /** Suggested commit message subject (style-matched). */
44
+ suggestedSubject: string;
45
+ /** Top 3 recommended reviewers. */
46
+ reviewers: Reviewer[];
47
+ /** Scope diagnosis — module count + verdict. */
48
+ scopeOK: boolean;
49
+ scopeMessage: string;
50
+ /** Past-regret warnings if similar past changes regretted. */
51
+ warnings: RegretWarning[];
52
+ }
53
+ /**
54
+ * Parse a unified diff string. Best-effort — handles standard `git diff`
55
+ * output. Skips binary diffs and file-mode changes.
56
+ */
57
+ export declare function parseDiff(diffText: string): ParsedDiff;
58
+ /**
59
+ * Look up the top reviewer per touched file (the file's largest contributor).
60
+ * Aggregates across files to find the people most worth requesting review from.
61
+ */
62
+ export declare function recommendReviewers(store: MnemeStore, files: string[], topN?: number): Reviewer[];
63
+ /**
64
+ * Suggest a commit message based on the diff shape + style of recent
65
+ * commits in the repo. Returns a Conventional-Commits-style subject line.
66
+ */
67
+ export declare function suggestSubject(diff: ParsedDiff, recentSubjects: string[]): string;
68
+ /** Diagnose scope creep. Touching > 3 modules is usually too much for one commit. */
69
+ export declare function checkScope(diff: ParsedDiff): {
70
+ scopeOK: boolean;
71
+ message: string;
72
+ };
73
+ /**
74
+ * Check past regrets: any commit touching the same files where the
75
+ * follow-up was a fix/revert? If so, surface as a warning.
76
+ */
77
+ export declare function checkPastRegrets(store: MnemeStore, files: string[]): RegretWarning[];
78
+ /** End-to-end coach: parse → recommend → diagnose. Pure-ish (reads the store). */
79
+ export declare function coach(store: MnemeStore, diffText: string): CoachAdvice;
80
+ //# sourceMappingURL=commit-coach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit-coach.d.ts","sourceRoot":"","sources":["../../src/insights/commit-coach.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIrD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,qEAAqE;IACrE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CACzE;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,IAAI,EAAE,UAAU,CAAC;IACjB,wDAAwD;IACxD,gBAAgB,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAmCtD;AAkBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,SAAI,GAAG,QAAQ,EAAE,CAyD3F;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CAiBjF;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAYlF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAmDpF;AAED,kFAAkF;AAClF,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAqBtE"}