@mneme-ai/core 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/insights/bus-factor.d.ts +58 -0
  6. package/dist/insights/bus-factor.d.ts.map +1 -0
  7. package/dist/insights/bus-factor.js +117 -0
  8. package/dist/insights/bus-factor.js.map +1 -0
  9. package/dist/insights/bus-factor.test.d.ts +2 -0
  10. package/dist/insights/bus-factor.test.d.ts.map +1 -0
  11. package/dist/insights/bus-factor.test.js +149 -0
  12. package/dist/insights/bus-factor.test.js.map +1 -0
  13. package/dist/insights/commit-coach.d.ts +80 -0
  14. package/dist/insights/commit-coach.d.ts.map +1 -0
  15. package/dist/insights/commit-coach.js +230 -0
  16. package/dist/insights/commit-coach.js.map +1 -0
  17. package/dist/insights/commit-coach.test.d.ts +2 -0
  18. package/dist/insights/commit-coach.test.d.ts.map +1 -0
  19. package/dist/insights/commit-coach.test.js +163 -0
  20. package/dist/insights/commit-coach.test.js.map +1 -0
  21. package/dist/insights/crystal-ball.d.ts +76 -0
  22. package/dist/insights/crystal-ball.d.ts.map +1 -0
  23. package/dist/insights/crystal-ball.js +219 -0
  24. package/dist/insights/crystal-ball.js.map +1 -0
  25. package/dist/insights/crystal-ball.test.d.ts +2 -0
  26. package/dist/insights/crystal-ball.test.d.ts.map +1 -0
  27. package/dist/insights/crystal-ball.test.js +157 -0
  28. package/dist/insights/crystal-ball.test.js.map +1 -0
  29. package/dist/insights/ghost.d.ts +80 -0
  30. package/dist/insights/ghost.d.ts.map +1 -0
  31. package/dist/insights/ghost.js +138 -0
  32. package/dist/insights/ghost.js.map +1 -0
  33. package/dist/insights/ghost.test.d.ts +2 -0
  34. package/dist/insights/ghost.test.d.ts.map +1 -0
  35. package/dist/insights/ghost.test.js +141 -0
  36. package/dist/insights/ghost.test.js.map +1 -0
  37. package/dist/insights/index.d.ts +8 -0
  38. package/dist/insights/index.d.ts.map +1 -1
  39. package/dist/insights/index.js +8 -0
  40. package/dist/insights/index.js.map +1 -1
  41. package/dist/insights/paradox.d.ts +36 -0
  42. package/dist/insights/paradox.d.ts.map +1 -0
  43. package/dist/insights/paradox.js +201 -0
  44. package/dist/insights/paradox.js.map +1 -0
  45. package/dist/insights/paradox.test.d.ts +2 -0
  46. package/dist/insights/paradox.test.d.ts.map +1 -0
  47. package/dist/insights/paradox.test.js +88 -0
  48. package/dist/insights/paradox.test.js.map +1 -0
  49. package/dist/insights/premortem.d.ts +73 -0
  50. package/dist/insights/premortem.d.ts.map +1 -0
  51. package/dist/insights/premortem.js +209 -0
  52. package/dist/insights/premortem.js.map +1 -0
  53. package/dist/insights/premortem.test.d.ts +2 -0
  54. package/dist/insights/premortem.test.d.ts.map +1 -0
  55. package/dist/insights/premortem.test.js +169 -0
  56. package/dist/insights/premortem.test.js.map +1 -0
  57. package/dist/insights/regret.d.ts +57 -0
  58. package/dist/insights/regret.d.ts.map +1 -0
  59. package/dist/insights/regret.js +137 -0
  60. package/dist/insights/regret.js.map +1 -0
  61. package/dist/insights/regret.test.d.ts +2 -0
  62. package/dist/insights/regret.test.d.ts.map +1 -0
  63. package/dist/insights/regret.test.js +153 -0
  64. package/dist/insights/regret.test.js.map +1 -0
  65. package/dist/insights/time-machine.d.ts +70 -0
  66. package/dist/insights/time-machine.d.ts.map +1 -0
  67. package/dist/insights/time-machine.js +177 -0
  68. package/dist/insights/time-machine.js.map +1 -0
  69. package/dist/insights/time-machine.test.d.ts +2 -0
  70. package/dist/insights/time-machine.test.d.ts.map +1 -0
  71. package/dist/insights/time-machine.test.js +141 -0
  72. package/dist/insights/time-machine.test.js.map +1 -0
  73. package/dist/insights/who-knows.d.ts +18 -0
  74. package/dist/insights/who-knows.d.ts.map +1 -1
  75. package/dist/insights/who-knows.js +29 -0
  76. package/dist/insights/who-knows.js.map +1 -1
  77. package/dist/insights/who-knows.test.js +63 -1
  78. package/dist/insights/who-knows.test.js.map +1 -1
  79. package/dist/quant/alpha.d.ts +87 -0
  80. package/dist/quant/alpha.d.ts.map +1 -0
  81. package/dist/quant/alpha.js +103 -0
  82. package/dist/quant/alpha.js.map +1 -0
  83. package/dist/quant/alpha.test.d.ts +2 -0
  84. package/dist/quant/alpha.test.d.ts.map +1 -0
  85. package/dist/quant/alpha.test.js +147 -0
  86. package/dist/quant/alpha.test.js.map +1 -0
  87. package/dist/quant/backtest.d.ts +57 -0
  88. package/dist/quant/backtest.d.ts.map +1 -0
  89. package/dist/quant/backtest.js +90 -0
  90. package/dist/quant/backtest.js.map +1 -0
  91. package/dist/quant/backtest.test.d.ts +2 -0
  92. package/dist/quant/backtest.test.d.ts.map +1 -0
  93. package/dist/quant/backtest.test.js +133 -0
  94. package/dist/quant/backtest.test.js.map +1 -0
  95. package/dist/quant/black-swan.d.ts +45 -0
  96. package/dist/quant/black-swan.d.ts.map +1 -0
  97. package/dist/quant/black-swan.js +112 -0
  98. package/dist/quant/black-swan.js.map +1 -0
  99. package/dist/quant/black-swan.test.d.ts +2 -0
  100. package/dist/quant/black-swan.test.d.ts.map +1 -0
  101. package/dist/quant/black-swan.test.js +131 -0
  102. package/dist/quant/black-swan.test.js.map +1 -0
  103. package/dist/quant/correlation-matrix.d.ts +54 -0
  104. package/dist/quant/correlation-matrix.d.ts.map +1 -0
  105. package/dist/quant/correlation-matrix.js +103 -0
  106. package/dist/quant/correlation-matrix.js.map +1 -0
  107. package/dist/quant/correlation-matrix.test.d.ts +2 -0
  108. package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
  109. package/dist/quant/correlation-matrix.test.js +118 -0
  110. package/dist/quant/correlation-matrix.test.js.map +1 -0
  111. package/dist/quant/drawdown.d.ts +51 -0
  112. package/dist/quant/drawdown.d.ts.map +1 -0
  113. package/dist/quant/drawdown.js +96 -0
  114. package/dist/quant/drawdown.js.map +1 -0
  115. package/dist/quant/drawdown.test.d.ts +2 -0
  116. package/dist/quant/drawdown.test.d.ts.map +1 -0
  117. package/dist/quant/drawdown.test.js +166 -0
  118. package/dist/quant/drawdown.test.js.map +1 -0
  119. package/dist/quant/greek.d.ts +55 -0
  120. package/dist/quant/greek.d.ts.map +1 -0
  121. package/dist/quant/greek.js +157 -0
  122. package/dist/quant/greek.js.map +1 -0
  123. package/dist/quant/greek.test.d.ts +2 -0
  124. package/dist/quant/greek.test.d.ts.map +1 -0
  125. package/dist/quant/greek.test.js +138 -0
  126. package/dist/quant/greek.test.js.map +1 -0
  127. package/dist/quant/implied-volatility.d.ts +65 -0
  128. package/dist/quant/implied-volatility.d.ts.map +1 -0
  129. package/dist/quant/implied-volatility.js +149 -0
  130. package/dist/quant/implied-volatility.js.map +1 -0
  131. package/dist/quant/implied-volatility.test.d.ts +2 -0
  132. package/dist/quant/implied-volatility.test.d.ts.map +1 -0
  133. package/dist/quant/implied-volatility.test.js +127 -0
  134. package/dist/quant/implied-volatility.test.js.map +1 -0
  135. package/dist/quant/index.d.ts +28 -0
  136. package/dist/quant/index.d.ts.map +1 -0
  137. package/dist/quant/index.js +28 -0
  138. package/dist/quant/index.js.map +1 -0
  139. package/dist/quant/insider-trading.d.ts +56 -0
  140. package/dist/quant/insider-trading.d.ts.map +1 -0
  141. package/dist/quant/insider-trading.js +129 -0
  142. package/dist/quant/insider-trading.js.map +1 -0
  143. package/dist/quant/insider-trading.test.d.ts +2 -0
  144. package/dist/quant/insider-trading.test.d.ts.map +1 -0
  145. package/dist/quant/insider-trading.test.js +130 -0
  146. package/dist/quant/insider-trading.test.js.map +1 -0
  147. package/dist/quant/moneyball.d.ts +48 -0
  148. package/dist/quant/moneyball.d.ts.map +1 -0
  149. package/dist/quant/moneyball.js +110 -0
  150. package/dist/quant/moneyball.js.map +1 -0
  151. package/dist/quant/moneyball.test.d.ts +2 -0
  152. package/dist/quant/moneyball.test.d.ts.map +1 -0
  153. package/dist/quant/moneyball.test.js +137 -0
  154. package/dist/quant/moneyball.test.js.map +1 -0
  155. package/dist/quant/tax-loss-harvest.d.ts +59 -0
  156. package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
  157. package/dist/quant/tax-loss-harvest.js +126 -0
  158. package/dist/quant/tax-loss-harvest.js.map +1 -0
  159. package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
  160. package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
  161. package/dist/quant/tax-loss-harvest.test.js +126 -0
  162. package/dist/quant/tax-loss-harvest.test.js.map +1 -0
  163. package/dist/retrieve/synthesize.d.ts.map +1 -1
  164. package/dist/retrieve/synthesize.js +56 -25
  165. package/dist/retrieve/synthesize.js.map +1 -1
  166. package/dist/retrieve/synthesize.test.js +26 -15
  167. package/dist/retrieve/synthesize.test.js.map +1 -1
  168. package/dist/store/schema.d.ts +2 -2
  169. package/dist/store/schema.d.ts.map +1 -1
  170. package/dist/store/schema.js +6 -2
  171. package/dist/store/schema.js.map +1 -1
  172. package/dist/store/sqlite.d.ts +2 -0
  173. package/dist/store/sqlite.d.ts.map +1 -1
  174. package/dist/store/sqlite.js +24 -0
  175. package/dist/store/sqlite.js.map +1 -1
  176. package/dist/store/sqlite.test.js +1 -1
  177. package/dist/util/index.d.ts +4 -0
  178. package/dist/util/index.d.ts.map +1 -1
  179. package/dist/util/index.js +26 -0
  180. package/dist/util/index.js.map +1 -1
  181. package/package.json +1 -1
@@ -0,0 +1,153 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { detectRegrets, summarizeRegrets } from "./regret.js";
3
+ const cmt = (hash, date, subject, body = "", files = []) => ({
4
+ hash,
5
+ shortHash: hash.slice(0, 7),
6
+ authorName: "alice",
7
+ authorEmail: "alice@x",
8
+ authorDate: `${date}T12:00:00Z`,
9
+ committerDate: `${date}T12:00:00Z`,
10
+ subject,
11
+ body,
12
+ parents: [],
13
+ files,
14
+ });
15
+ describe("detectRegrets — revert (strongest signal)", () => {
16
+ it("detects explicit revert that names the shipped commit hash", () => {
17
+ const commits = [
18
+ cmt("a1b2c3d", "2024-08-01", "feat: enable HTTP/3", "", ["src/edge.ts"]),
19
+ cmt("e4f5g6h", "2024-08-03", "Revert \"feat: enable HTTP/3\"", "Reverts a1b2c3d — broke mobile", ["src/edge.ts"]),
20
+ ];
21
+ const regrets = detectRegrets(commits, { windowDays: 7 });
22
+ expect(regrets).toHaveLength(1);
23
+ expect(regrets[0].kind).toBe("revert");
24
+ expect(regrets[0].daysToFix).toBeCloseTo(2, 0);
25
+ });
26
+ it("detects revert from shared-files heuristic when subject is generic", () => {
27
+ const commits = [
28
+ cmt("a1", "2024-08-01", "feat: new caching", "", ["src/cache.ts"]),
29
+ cmt("a2", "2024-08-04", "revert caching change", "", ["src/cache.ts"]),
30
+ ];
31
+ expect(detectRegrets(commits)[0].kind).toBe("revert");
32
+ });
33
+ });
34
+ describe("detectRegrets — hotfix and fix kinds", () => {
35
+ it("flags hotfix when follow-up has 'hotfix' marker AND shares files", () => {
36
+ const commits = [
37
+ cmt("a1", "2024-08-01", "refactor: simplify session middleware", "", ["src/auth.ts"]),
38
+ cmt("a2", "2024-08-01", "hotfix: CSRF check disappeared in refactor", "", ["src/auth.ts"]),
39
+ ];
40
+ expect(detectRegrets(commits)[0].kind).toBe("hotfix");
41
+ });
42
+ it("flags 'fix' kind when follow-up is a generic fix touching same files", () => {
43
+ const commits = [
44
+ cmt("a1", "2024-08-01", "perf: cache user lookup", "", ["src/users.ts"]),
45
+ cmt("a2", "2024-08-04", "fix: cache invalidation race", "", ["src/users.ts"]),
46
+ ];
47
+ expect(detectRegrets(commits)[0].kind).toBe("fix");
48
+ });
49
+ it("doesn't double-flag commits that ARE themselves fixes", () => {
50
+ const commits = [
51
+ cmt("a1", "2024-08-01", "fix: typo", "", ["README.md"]),
52
+ cmt("a2", "2024-08-02", "fix: another typo", "", ["README.md"]),
53
+ ];
54
+ // Neither is a regret — both are fix-shaped subjects.
55
+ expect(detectRegrets(commits)).toEqual([]);
56
+ });
57
+ });
58
+ describe("detectRegrets — window enforcement", () => {
59
+ it("respects the windowDays cutoff", () => {
60
+ const commits = [
61
+ cmt("a1", "2024-08-01", "feat: thing", "", ["x.ts"]),
62
+ cmt("a2", "2024-08-15", "fix: thing broke", "", ["x.ts"]), // 14 days later
63
+ ];
64
+ expect(detectRegrets(commits, { windowDays: 7 })).toEqual([]);
65
+ expect(detectRegrets(commits, { windowDays: 30 })).toHaveLength(1);
66
+ });
67
+ it("respects minDaysToFix lower bound (filter out same-day micro-fixes)", () => {
68
+ const commits = [
69
+ cmt("a1", "2024-08-01", "feat: thing", "", ["x.ts"]),
70
+ cmt("a2", "2024-08-01", "fix: typo", "", ["x.ts"]),
71
+ ];
72
+ expect(detectRegrets(commits, { minDaysToFix: 1 })).toEqual([]);
73
+ expect(detectRegrets(commits, { minDaysToFix: 0 })).toHaveLength(1);
74
+ });
75
+ });
76
+ describe("detectRegrets — only one regret per shipped commit", () => {
77
+ it("returns the first follow-up only (avoids double-counting)", () => {
78
+ const commits = [
79
+ cmt("a1", "2024-08-01", "feat: thing", "", ["x.ts"]),
80
+ cmt("a2", "2024-08-02", "fix: first attempt at fix", "", ["x.ts"]),
81
+ cmt("a3", "2024-08-04", "fix: second attempt", "", ["x.ts"]),
82
+ ];
83
+ const regrets = detectRegrets(commits);
84
+ expect(regrets).toHaveLength(1);
85
+ expect(regrets[0].followup.shortHash).toBe("a2");
86
+ });
87
+ });
88
+ describe("detectRegrets — output ordering and content", () => {
89
+ it("sorts results by ship date descending (newest regret first)", () => {
90
+ const commits = [
91
+ cmt("old1", "2024-01-01", "feat: a", "", ["x.ts"]),
92
+ cmt("old2", "2024-01-02", "fix: a broke", "", ["x.ts"]),
93
+ cmt("new1", "2024-08-01", "feat: b", "", ["y.ts"]),
94
+ cmt("new2", "2024-08-03", "fix: b broke", "", ["y.ts"]),
95
+ ];
96
+ const regrets = detectRegrets(commits);
97
+ expect(regrets).toHaveLength(2);
98
+ expect(regrets[0].shipped.shortHash).toBe("new1");
99
+ expect(regrets[1].shipped.shortHash).toBe("old1");
100
+ });
101
+ it("attaches a lesson string when the follow-up body has explanatory text", () => {
102
+ const commits = [
103
+ cmt("a1", "2024-08-01", "feat: thing", "", ["x.ts"]),
104
+ cmt("a2", "2024-08-02", "fix: thing crashed", "the cache invalidation was racing with the eviction sweep", ["x.ts"]),
105
+ ];
106
+ const r = detectRegrets(commits)[0];
107
+ expect(r.lesson).toContain("cache invalidation");
108
+ });
109
+ });
110
+ describe("summarizeRegrets — repo-level metrics", () => {
111
+ it("computes regretRate over non-fix shipped commits", () => {
112
+ const commits = [
113
+ cmt("a1", "2024-08-01", "feat: A", "", ["x.ts"]),
114
+ cmt("a2", "2024-08-02", "fix: A broke", "", ["x.ts"]),
115
+ cmt("a3", "2024-08-03", "feat: B", "", ["y.ts"]),
116
+ cmt("a4", "2024-08-04", "feat: C", "", ["z.ts"]),
117
+ ];
118
+ const regrets = detectRegrets(commits);
119
+ const summary = summarizeRegrets(commits, regrets);
120
+ // Non-fix shipped: a1, a3, a4 → 3 candidates. 1 regret. → 1/3 ≈ 0.33
121
+ expect(summary.totalShipped).toBe(3);
122
+ expect(summary.totalRegrets).toBe(1);
123
+ expect(summary.regretRate).toBeCloseTo(1 / 3, 2);
124
+ });
125
+ it("counts byKind correctly", () => {
126
+ const commits = [
127
+ cmt("a1", "2024-08-01", "feat: A", "", ["x.ts"]),
128
+ cmt("a2", "2024-08-02", "Revert \"feat: A\"", "", ["x.ts"]),
129
+ cmt("a3", "2024-08-03", "feat: B", "", ["y.ts"]),
130
+ cmt("a4", "2024-08-04", "hotfix: B explosion", "", ["y.ts"]),
131
+ ];
132
+ const regrets = detectRegrets(commits);
133
+ const summary = summarizeRegrets(commits, regrets);
134
+ expect(summary.byKind.revert).toBe(1);
135
+ expect(summary.byKind.hotfix).toBe(1);
136
+ });
137
+ it("reports averageDaysToFix", () => {
138
+ const commits = [
139
+ cmt("a1", "2024-08-01", "feat: A", "", ["x.ts"]),
140
+ cmt("a2", "2024-08-03", "fix: A", "", ["x.ts"]), // 2 days
141
+ cmt("a3", "2024-08-10", "feat: B", "", ["y.ts"]),
142
+ cmt("a4", "2024-08-14", "fix: B", "", ["y.ts"]), // 4 days
143
+ ];
144
+ const summary = summarizeRegrets(commits, detectRegrets(commits));
145
+ expect(summary.averageDaysToFix).toBeCloseTo(3, 1);
146
+ });
147
+ it("returns 0 metrics when there are no regrets", () => {
148
+ const summary = summarizeRegrets([cmt("a", "2024-08-01", "feat: stable", "", ["x"])], []);
149
+ expect(summary.regretRate).toBe(0);
150
+ expect(summary.averageDaysToFix).toBe(0);
151
+ });
152
+ });
153
+ //# sourceMappingURL=regret.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regret.test.js","sourceRoot":"","sources":["../../src/insights/regret.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * `mneme time-machine <file>` — narrate a file's evolution as discrete
3
+ * epochs, not a flat diff log. Each epoch is a meaningful era in the
4
+ * file's life: when it was born, when it was rewritten, when it stabilized,
5
+ * when it broke things, when it was abandoned.
6
+ *
7
+ * What makes this novel:
8
+ * - Most tools show `git log file.ts` — a flat list. Time Machine
9
+ * groups commits into ERAS based on intent (rewrite, fix, polish,
10
+ * stable plateau).
11
+ * - Detects "rewrite events" (large simultaneous insertions+deletions)
12
+ * vs. "polish events" (small surgical changes) vs. "stable plateaus"
13
+ * (long quiet stretches).
14
+ * - Surfaces the WHY for each epoch by extracting the most informative
15
+ * commit message of that era.
16
+ * - Computes "narrative health" — how much of the history is rewrite
17
+ * vs. evolution vs. firefight.
18
+ *
19
+ * Pure data extraction. The CLI renders the timeline; an LLM (optional)
20
+ * can polish the prose.
21
+ */
22
+ import type { Commit, FileChange } from "../types.js";
23
+ export type EpochKind = "birth" | "rewrite" | "evolution" | "firefight" | "polish" | "plateau" | "twilight";
24
+ export interface FileEpoch {
25
+ kind: EpochKind;
26
+ /** Sequential number, 1-indexed. */
27
+ index: number;
28
+ /** Commits defining this epoch (chronological). */
29
+ commits: Commit[];
30
+ /** The most informative commit of the epoch — used for narration. */
31
+ defining: Commit;
32
+ fromDate: string;
33
+ toDate: string;
34
+ /** Total insertions across the epoch. */
35
+ insertions: number;
36
+ /** Total deletions across the epoch. */
37
+ deletions: number;
38
+ /** Days spanned by this epoch. */
39
+ spanDays: number;
40
+ /** A short prose label — the WHY of this epoch. */
41
+ label: string;
42
+ }
43
+ export interface TimeMachineResult {
44
+ filePath: string;
45
+ totalCommits: number;
46
+ totalSpanDays: number;
47
+ epochs: FileEpoch[];
48
+ /**
49
+ * Health balance across epochs:
50
+ * - rewriteRatio: fraction of commits in rewrite epochs (high = unstable)
51
+ * - firefightRatio: fraction in firefight epochs (high = pain)
52
+ * - polishRatio: fraction in polish/plateau (high = mature)
53
+ */
54
+ health: {
55
+ rewriteRatio: number;
56
+ firefightRatio: number;
57
+ polishRatio: number;
58
+ };
59
+ }
60
+ /**
61
+ * Compute file epochs from a chronological list of commits + per-commit
62
+ * file changes for the target file. Caller is responsible for filtering
63
+ * commits to ones that actually touched `filePath`.
64
+ */
65
+ export declare function buildTimeMachine(filePath: string, commits: Commit[], changes: Map<string, FileChange>, // commitHash -> file change for filePath
66
+ opts?: {
67
+ plateauDays?: number;
68
+ rewriteChurnLines?: number;
69
+ }): TimeMachineResult;
70
+ //# sourceMappingURL=time-machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-machine.d.ts","sourceRoot":"","sources":["../../src/insights/time-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,SAAS,GACT,WAAW,GACX,WAAW,GACX,QAAQ,GACR,SAAS,GACT,UAAU,CAAC;AAEf,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB;;;;;OAKG;IACH,MAAM,EAAE;QACN,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,yCAAyC;AAC3E,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,iBAAiB,CAgJnB"}
@@ -0,0 +1,177 @@
1
+ const FIRE_RE = /\b(fix(?:es|ed)?|hotfix|revert|rollback|broken|crash|regression|emergency|urgent|critical|p[01])\b/i;
2
+ const REWRITE_RE = /\b(rewrite|refactor|overhaul|redesign|migrate|replace[ds]?|move(?:d)? to)\b/i;
3
+ const POLISH_RE = /\b(typo|format|lint|rename|comment|whitespace|cleanup|style|chore)\b/i;
4
+ /**
5
+ * Compute file epochs from a chronological list of commits + per-commit
6
+ * file changes for the target file. Caller is responsible for filtering
7
+ * commits to ones that actually touched `filePath`.
8
+ */
9
+ export function buildTimeMachine(filePath, commits, changes, // commitHash -> file change for filePath
10
+ opts = {}) {
11
+ const plateauDays = opts.plateauDays ?? 60;
12
+ const rewriteChurnLines = opts.rewriteChurnLines ?? 80;
13
+ const sorted = [...commits].sort((a, b) => a.authorDate.localeCompare(b.authorDate));
14
+ if (sorted.length === 0) {
15
+ return {
16
+ filePath,
17
+ totalCommits: 0,
18
+ totalSpanDays: 0,
19
+ epochs: [],
20
+ health: { rewriteRatio: 0, firefightRatio: 0, polishRatio: 0 },
21
+ };
22
+ }
23
+ const flavorOf = (c) => {
24
+ const text = `${c.subject}\n${c.body || ""}`;
25
+ const fc = changes.get(c.hash);
26
+ const churn = fc ? fc.insertions + fc.deletions : 0;
27
+ if (REWRITE_RE.test(text) || churn >= rewriteChurnLines)
28
+ return "rewrite";
29
+ if (FIRE_RE.test(text))
30
+ return "firefight";
31
+ if (POLISH_RE.test(text) || churn <= 5)
32
+ return "polish";
33
+ return "evolution";
34
+ };
35
+ const epochs = [];
36
+ let buf = [sorted[0]];
37
+ let bufFlavor = sorted[0].parents.length === 0 || epochs.length === 0 ? "birth" : flavorOf(sorted[0]);
38
+ // birth applies only to the very first commit
39
+ bufFlavor = "birth";
40
+ let prevDate = new Date(sorted[0].authorDate).getTime();
41
+ const flush = (kind) => {
42
+ const commitsInEpoch = buf;
43
+ if (commitsInEpoch.length === 0)
44
+ return;
45
+ let ins = 0;
46
+ let del = 0;
47
+ for (const c of commitsInEpoch) {
48
+ const fc = changes.get(c.hash);
49
+ if (fc) {
50
+ ins += fc.insertions;
51
+ del += fc.deletions;
52
+ }
53
+ }
54
+ const fromDate = commitsInEpoch[0].authorDate.slice(0, 10);
55
+ const toDate = commitsInEpoch[commitsInEpoch.length - 1].authorDate.slice(0, 10);
56
+ const spanDays = Math.max(0, Math.round((new Date(toDate).getTime() - new Date(fromDate).getTime()) / 86_400_000));
57
+ const defining = pickDefiningCommit(commitsInEpoch, kind);
58
+ epochs.push({
59
+ kind,
60
+ index: epochs.length + 1,
61
+ commits: commitsInEpoch,
62
+ defining,
63
+ fromDate,
64
+ toDate,
65
+ insertions: ins,
66
+ deletions: del,
67
+ spanDays,
68
+ label: labelEpoch(kind, defining, ins + del),
69
+ });
70
+ };
71
+ for (let i = 1; i < sorted.length; i++) {
72
+ const c = sorted[i];
73
+ const cTime = new Date(c.authorDate).getTime();
74
+ const gapDays = (cTime - prevDate) / 86_400_000;
75
+ const flavor = flavorOf(c);
76
+ // Long quiet gap → emit plateau epoch covering the gap
77
+ if (gapDays >= plateauDays) {
78
+ flush(bufFlavor);
79
+ epochs.push({
80
+ kind: "plateau",
81
+ index: epochs.length + 1,
82
+ commits: [],
83
+ defining: buf[buf.length - 1],
84
+ fromDate: buf[buf.length - 1].authorDate.slice(0, 10),
85
+ toDate: c.authorDate.slice(0, 10),
86
+ insertions: 0,
87
+ deletions: 0,
88
+ spanDays: Math.round(gapDays),
89
+ label: `quiet stretch — ${Math.round(gapDays)} days untouched`,
90
+ });
91
+ buf = [c];
92
+ bufFlavor = flavor;
93
+ }
94
+ else if (flavor !== bufFlavor) {
95
+ // flavor change → close current epoch, start new one
96
+ flush(bufFlavor);
97
+ buf = [c];
98
+ bufFlavor = flavor;
99
+ }
100
+ else {
101
+ buf.push(c);
102
+ }
103
+ prevDate = cTime;
104
+ }
105
+ flush(bufFlavor);
106
+ // Mark trailing plateau as "twilight" if file is quiet at end (>plateauDays
107
+ // since last touch).
108
+ const last = epochs[epochs.length - 1];
109
+ if (last && last.kind === "plateau" && last.spanDays >= plateauDays * 2) {
110
+ last.kind = "twilight";
111
+ last.label = `twilight — ${last.spanDays} days since last touch`;
112
+ }
113
+ // Compute health ratios
114
+ const totalCommits = sorted.length;
115
+ let rewrite = 0;
116
+ let firefight = 0;
117
+ let polish = 0;
118
+ for (const e of epochs) {
119
+ if (e.kind === "rewrite")
120
+ rewrite += e.commits.length;
121
+ else if (e.kind === "firefight")
122
+ firefight += e.commits.length;
123
+ else if (e.kind === "polish" || e.kind === "plateau")
124
+ polish += e.commits.length;
125
+ }
126
+ const totalSpanDays = Math.max(0, Math.round((new Date(sorted[sorted.length - 1].authorDate).getTime() -
127
+ new Date(sorted[0].authorDate).getTime()) /
128
+ 86_400_000));
129
+ return {
130
+ filePath,
131
+ totalCommits,
132
+ totalSpanDays,
133
+ epochs,
134
+ health: {
135
+ rewriteRatio: totalCommits === 0 ? 0 : rewrite / totalCommits,
136
+ firefightRatio: totalCommits === 0 ? 0 : firefight / totalCommits,
137
+ polishRatio: totalCommits === 0 ? 0 : polish / totalCommits,
138
+ },
139
+ };
140
+ }
141
+ function pickDefiningCommit(commits, kind) {
142
+ if (commits.length === 0)
143
+ throw new Error("empty epoch");
144
+ if (kind === "birth")
145
+ return commits[0];
146
+ if (kind === "rewrite") {
147
+ // longest body / most descriptive subject wins
148
+ return [...commits].sort((a, b) => (b.body?.length ?? 0) + b.subject.length - ((a.body?.length ?? 0) + a.subject.length))[0];
149
+ }
150
+ // For evolution/polish/firefight, pick the commit whose subject is longest
151
+ // (typically more informative than "fix typo")
152
+ return [...commits].sort((a, b) => b.subject.length - a.subject.length)[0];
153
+ }
154
+ function labelEpoch(kind, defining, churn) {
155
+ switch (kind) {
156
+ case "birth":
157
+ return `born — "${truncate(defining.subject, 60)}"`;
158
+ case "rewrite":
159
+ return `rewrite — "${truncate(defining.subject, 60)}" (${churn} lines)`;
160
+ case "evolution":
161
+ return `evolution — "${truncate(defining.subject, 60)}"`;
162
+ case "firefight":
163
+ return `firefight — "${truncate(defining.subject, 60)}"`;
164
+ case "polish":
165
+ return `polish — "${truncate(defining.subject, 60)}"`;
166
+ case "plateau":
167
+ return `plateau — quiet`;
168
+ case "twilight":
169
+ return `twilight — possibly abandoned`;
170
+ }
171
+ }
172
+ function truncate(s, n) {
173
+ if (s.length <= n)
174
+ return s;
175
+ return s.slice(0, n - 1).trimEnd() + "…";
176
+ }
177
+ //# sourceMappingURL=time-machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-machine.js","sourceRoot":"","sources":["../../src/insights/time-machine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=time-machine.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-machine.test.d.ts","sourceRoot":"","sources":["../../src/insights/time-machine.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildTimeMachine } from "./time-machine.js";
3
+ function mkCommit(p) {
4
+ return {
5
+ shortHash: p.hash.slice(0, 7),
6
+ authorName: "Test",
7
+ authorEmail: "t@e.com",
8
+ committerDate: p.authorDate,
9
+ body: p.body ?? "",
10
+ files: ["src/x.ts"],
11
+ parents: p.parents ?? [],
12
+ ...p,
13
+ };
14
+ }
15
+ function mkChange(commitHash, ins, del) {
16
+ return {
17
+ commitHash,
18
+ path: "src/x.ts",
19
+ changeKind: "M",
20
+ insertions: ins,
21
+ deletions: del,
22
+ };
23
+ }
24
+ function chmap(...changes) {
25
+ const m = new Map();
26
+ for (const c of changes)
27
+ m.set(c.commitHash, c);
28
+ return m;
29
+ }
30
+ describe("buildTimeMachine", () => {
31
+ it("returns empty result when there are no commits", () => {
32
+ const result = buildTimeMachine("src/x.ts", [], new Map());
33
+ expect(result.epochs).toHaveLength(0);
34
+ expect(result.totalCommits).toBe(0);
35
+ });
36
+ it("first commit is always a 'birth' epoch", () => {
37
+ const commits = [mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "initial" })];
38
+ const result = buildTimeMachine("src/x.ts", commits, chmap(mkChange("a1", 50, 0)));
39
+ expect(result.epochs).toHaveLength(1);
40
+ expect(result.epochs[0].kind).toBe("birth");
41
+ expect(result.epochs[0].label).toContain("born");
42
+ });
43
+ it("detects rewrite epoch from large churn", () => {
44
+ const commits = [
45
+ mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "initial" }),
46
+ mkCommit({ hash: "a2", authorDate: "2024-01-05", subject: "rewrite to use streams" }),
47
+ ];
48
+ const changes = chmap(mkChange("a1", 50, 0), mkChange("a2", 200, 180));
49
+ const result = buildTimeMachine("src/x.ts", commits, changes);
50
+ const rewriteEpoch = result.epochs.find((e) => e.kind === "rewrite");
51
+ expect(rewriteEpoch).toBeDefined();
52
+ expect(rewriteEpoch.insertions + rewriteEpoch.deletions).toBeGreaterThan(100);
53
+ });
54
+ it("detects firefight epoch from fix/hotfix keywords", () => {
55
+ const commits = [
56
+ mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "initial" }),
57
+ mkCommit({ hash: "a2", authorDate: "2024-01-02", subject: "evolve feature" }),
58
+ mkCommit({ hash: "a3", authorDate: "2024-01-03", subject: "hotfix critical bug" }),
59
+ mkCommit({ hash: "a4", authorDate: "2024-01-04", subject: "fix regression" }),
60
+ ];
61
+ const changes = chmap(mkChange("a1", 50, 0), mkChange("a2", 20, 10), mkChange("a3", 5, 2), mkChange("a4", 8, 4));
62
+ const result = buildTimeMachine("src/x.ts", commits, changes);
63
+ const firefight = result.epochs.find((e) => e.kind === "firefight");
64
+ expect(firefight).toBeDefined();
65
+ expect(firefight.commits.length).toBeGreaterThanOrEqual(2);
66
+ });
67
+ it("detects polish epoch from small surgical changes", () => {
68
+ const commits = [
69
+ mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "initial" }),
70
+ mkCommit({ hash: "a2", authorDate: "2024-01-02", subject: "fix typo" }),
71
+ mkCommit({ hash: "a3", authorDate: "2024-01-03", subject: "format with prettier" }),
72
+ ];
73
+ const changes = chmap(mkChange("a1", 50, 0), mkChange("a2", 1, 1), mkChange("a3", 3, 3));
74
+ const result = buildTimeMachine("src/x.ts", commits, changes);
75
+ const polish = result.epochs.find((e) => e.kind === "polish");
76
+ expect(polish).toBeDefined();
77
+ });
78
+ it("inserts a plateau epoch when there is a long quiet gap", () => {
79
+ const commits = [
80
+ mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "initial" }),
81
+ mkCommit({ hash: "a2", authorDate: "2024-06-01", subject: "minor tweak" }),
82
+ ];
83
+ const changes = chmap(mkChange("a1", 50, 0), mkChange("a2", 5, 2));
84
+ const result = buildTimeMachine("src/x.ts", commits, changes, { plateauDays: 30 });
85
+ const plateau = result.epochs.find((e) => e.kind === "plateau" || e.kind === "twilight");
86
+ expect(plateau).toBeDefined();
87
+ expect(plateau.spanDays).toBeGreaterThanOrEqual(30);
88
+ });
89
+ it("computes health ratios summing to ≤ 1", () => {
90
+ const commits = Array.from({ length: 6 }, (_, i) => mkCommit({
91
+ hash: `c${i}`,
92
+ authorDate: `2024-01-${(i + 1).toString().padStart(2, "0")}`,
93
+ subject: i === 0 ? "init" : i % 2 === 0 ? "fix bug" : "refactor module",
94
+ }));
95
+ const changes = chmap(...commits.map((c, i) => mkChange(c.hash, i % 2 === 1 ? 200 : 5, i % 2 === 1 ? 100 : 5)));
96
+ const result = buildTimeMachine("src/x.ts", commits, changes);
97
+ const sum = result.health.rewriteRatio + result.health.firefightRatio + result.health.polishRatio;
98
+ expect(sum).toBeGreaterThanOrEqual(0);
99
+ expect(sum).toBeLessThanOrEqual(1);
100
+ });
101
+ it("picks defining commit by descriptiveness within an epoch", () => {
102
+ const commits = [
103
+ mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "init" }),
104
+ mkCommit({ hash: "a2", authorDate: "2024-01-02", subject: "x" }),
105
+ mkCommit({
106
+ hash: "a3",
107
+ authorDate: "2024-01-03",
108
+ subject: "evolve handler with comprehensive validation logic",
109
+ }),
110
+ ];
111
+ const changes = chmap(mkChange("a1", 30, 0), mkChange("a2", 5, 2), mkChange("a3", 20, 8));
112
+ const result = buildTimeMachine("src/x.ts", commits, changes);
113
+ // The evolution epoch should pick the longer subject
114
+ const evo = result.epochs.find((e) => e.kind === "evolution");
115
+ if (evo) {
116
+ expect(evo.defining.subject.length).toBeGreaterThanOrEqual(20);
117
+ }
118
+ });
119
+ it("produces non-empty labels for every epoch", () => {
120
+ const commits = [
121
+ mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "first" }),
122
+ mkCommit({ hash: "a2", authorDate: "2024-01-10", subject: "rewrite the parsing pipeline entirely" }),
123
+ ];
124
+ const result = buildTimeMachine("src/x.ts", commits, chmap(mkChange("a1", 50, 0), mkChange("a2", 300, 250)));
125
+ for (const e of result.epochs) {
126
+ expect(e.label.length).toBeGreaterThan(0);
127
+ }
128
+ });
129
+ it("orders epochs chronologically by index", () => {
130
+ const commits = [
131
+ mkCommit({ hash: "a1", authorDate: "2024-01-01", subject: "init" }),
132
+ mkCommit({ hash: "a2", authorDate: "2024-01-02", subject: "fix bug" }),
133
+ mkCommit({ hash: "a3", authorDate: "2024-01-03", subject: "refactor heavily" }),
134
+ ];
135
+ const result = buildTimeMachine("src/x.ts", commits, chmap(mkChange("a1", 30, 0), mkChange("a2", 5, 2), mkChange("a3", 200, 100)));
136
+ for (let i = 0; i < result.epochs.length; i++) {
137
+ expect(result.epochs[i].index).toBe(i + 1);
138
+ }
139
+ });
140
+ });
141
+ //# sourceMappingURL=time-machine.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-machine.test.js","sourceRoot":"","sources":["../../src/insights/time-machine.test.ts"],"names":[],"mappings":"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"}
@@ -45,4 +45,22 @@ export declare function whoKnows(store: MnemeStore, opts: WhoKnowsOptions): Expe
45
45
  /** log-based commit weight × recency multiplier. Pure function, easy to test. */
46
46
  export declare function scoreCandidate(commitCount: number, lastTouchIso: string, now: Date): number;
47
47
  export declare function tierOf(commitCount: number, lastTouchIso: string, now: Date): ExpertCandidate["tier"];
48
+ /**
49
+ * Verdict-shaped summary built from the full candidate list. Surfaces the
50
+ * single most likely person to ask + a confidence percentage + a fallback
51
+ * recommendation, instead of letting the user scan a list of names.
52
+ */
53
+ export interface ExpertVerdict {
54
+ /** The single most likely expert (or undefined when no candidates). */
55
+ topExpert?: ExpertCandidate;
56
+ /** Confidence percentage 0..100 — share of total commits attributed to the top. */
57
+ confidencePct: number;
58
+ /** Backup expert if the top is stale or has insufficient share. */
59
+ backup?: ExpertCandidate;
60
+ /** A 1-line risk note when the top expert is stale or shares too thinly. */
61
+ risk?: string;
62
+ /** Total commits matched across all candidates (sum of commitCount). */
63
+ totalCommits: number;
64
+ }
65
+ export declare function whoKnowsVerdict(candidates: ExpertCandidate[]): ExpertVerdict;
48
66
  //# sourceMappingURL=who-knows.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"who-knows.d.ts","sourceRoot":"","sources":["../../src/insights/who-knows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;CACxD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAKD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,GAAG,eAAe,EAAE,CA6DpF;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,MAAM,CAQ3F;AAED,wBAAgB,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,CAOpG"}
1
+ {"version":3,"file":"who-knows.d.ts","sourceRoot":"","sources":["../../src/insights/who-knows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;CACxD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAKD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,GAAG,eAAe,EAAE,CA6DpF;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,MAAM,CAQ3F;AAED,wBAAgB,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,CAOpG;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,mFAAmF;IACnF,aAAa,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,aAAa,CAoB5E"}