@mneme-ai/core 0.11.1 → 0.13.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 (77) hide show
  1. package/dist/insights/chronicle.d.ts +41 -0
  2. package/dist/insights/chronicle.d.ts.map +1 -0
  3. package/dist/insights/chronicle.js +169 -0
  4. package/dist/insights/chronicle.js.map +1 -0
  5. package/dist/insights/chronicle.test.d.ts +2 -0
  6. package/dist/insights/chronicle.test.d.ts.map +1 -0
  7. package/dist/insights/chronicle.test.js +92 -0
  8. package/dist/insights/chronicle.test.js.map +1 -0
  9. package/dist/insights/cluster.d.ts +52 -0
  10. package/dist/insights/cluster.d.ts.map +1 -0
  11. package/dist/insights/cluster.js +154 -0
  12. package/dist/insights/cluster.js.map +1 -0
  13. package/dist/insights/cluster.test.d.ts +2 -0
  14. package/dist/insights/cluster.test.d.ts.map +1 -0
  15. package/dist/insights/cluster.test.js +106 -0
  16. package/dist/insights/cluster.test.js.map +1 -0
  17. package/dist/insights/constellation.d.ts +63 -0
  18. package/dist/insights/constellation.d.ts.map +1 -0
  19. package/dist/insights/constellation.js +147 -0
  20. package/dist/insights/constellation.js.map +1 -0
  21. package/dist/insights/constellation.test.d.ts +2 -0
  22. package/dist/insights/constellation.test.d.ts.map +1 -0
  23. package/dist/insights/constellation.test.js +105 -0
  24. package/dist/insights/constellation.test.js.map +1 -0
  25. package/dist/insights/dna.d.ts +100 -0
  26. package/dist/insights/dna.d.ts.map +1 -0
  27. package/dist/insights/dna.js +264 -0
  28. package/dist/insights/dna.js.map +1 -0
  29. package/dist/insights/dna.test.d.ts +2 -0
  30. package/dist/insights/dna.test.d.ts.map +1 -0
  31. package/dist/insights/dna.test.js +142 -0
  32. package/dist/insights/dna.test.js.map +1 -0
  33. package/dist/insights/drift.d.ts +43 -0
  34. package/dist/insights/drift.d.ts.map +1 -0
  35. package/dist/insights/drift.js +170 -0
  36. package/dist/insights/drift.js.map +1 -0
  37. package/dist/insights/drift.test.d.ts +2 -0
  38. package/dist/insights/drift.test.d.ts.map +1 -0
  39. package/dist/insights/drift.test.js +126 -0
  40. package/dist/insights/drift.test.js.map +1 -0
  41. package/dist/insights/export-bundle.d.ts +53 -0
  42. package/dist/insights/export-bundle.d.ts.map +1 -0
  43. package/dist/insights/export-bundle.js +156 -0
  44. package/dist/insights/export-bundle.js.map +1 -0
  45. package/dist/insights/export-bundle.test.d.ts +2 -0
  46. package/dist/insights/export-bundle.test.d.ts.map +1 -0
  47. package/dist/insights/export-bundle.test.js +92 -0
  48. package/dist/insights/export-bundle.test.js.map +1 -0
  49. package/dist/insights/index.d.ts +9 -0
  50. package/dist/insights/index.d.ts.map +1 -1
  51. package/dist/insights/index.js +9 -0
  52. package/dist/insights/index.js.map +1 -1
  53. package/dist/insights/manage.d.ts +60 -0
  54. package/dist/insights/manage.d.ts.map +1 -0
  55. package/dist/insights/manage.js +110 -0
  56. package/dist/insights/manage.js.map +1 -0
  57. package/dist/insights/manage.test.d.ts +2 -0
  58. package/dist/insights/manage.test.d.ts.map +1 -0
  59. package/dist/insights/manage.test.js +94 -0
  60. package/dist/insights/manage.test.js.map +1 -0
  61. package/dist/insights/network.d.ts +59 -0
  62. package/dist/insights/network.d.ts.map +1 -0
  63. package/dist/insights/network.js +231 -0
  64. package/dist/insights/network.js.map +1 -0
  65. package/dist/insights/network.test.d.ts +2 -0
  66. package/dist/insights/network.test.d.ts.map +1 -0
  67. package/dist/insights/network.test.js +100 -0
  68. package/dist/insights/network.test.js.map +1 -0
  69. package/dist/insights/oracle.d.ts +59 -0
  70. package/dist/insights/oracle.d.ts.map +1 -0
  71. package/dist/insights/oracle.js +0 -0
  72. package/dist/insights/oracle.js.map +1 -0
  73. package/dist/insights/oracle.test.d.ts +2 -0
  74. package/dist/insights/oracle.test.d.ts.map +1 -0
  75. package/dist/insights/oracle.test.js +133 -0
  76. package/dist/insights/oracle.test.js.map +1 -0
  77. package/package.json +1 -1
@@ -0,0 +1,41 @@
1
+ /**
2
+ * `mneme chronicle` — auto-generate a narrative documentary of the repo,
3
+ * told as chapters. Detects significant epochs across the WHOLE repo
4
+ * (not a single file like time-machine), names each chapter, and emits
5
+ * markdown ready to export to PDF/EPUB.
6
+ *
7
+ * Pure data extraction. CLI renders + optional LLM polish for prose.
8
+ */
9
+ import type { Commit } from "../types.js";
10
+ export interface Chapter {
11
+ number: number;
12
+ title: string;
13
+ /** Sub-title evoking what happened (extractive). */
14
+ subtitle: string;
15
+ fromDate: string;
16
+ toDate: string;
17
+ spanDays: number;
18
+ commits: Commit[];
19
+ /** Top contributor of this chapter. */
20
+ protagonist: string;
21
+ /** Opening paragraph (extractive — first informative commit). */
22
+ opening: string;
23
+ /** Closing paragraph. */
24
+ closing: string;
25
+ }
26
+ export interface Chronicle {
27
+ totalCommits: number;
28
+ totalDays: number;
29
+ chapters: Chapter[];
30
+ }
31
+ /**
32
+ * Build chapters from commit history. We split history into epochs
33
+ * detected by long gaps OR by sudden churn shifts (rewrite events).
34
+ */
35
+ export declare function buildChronicle(commits: Commit[], opts?: {
36
+ gapDays?: number;
37
+ minChapterCommits?: number;
38
+ }): Chronicle;
39
+ /** Render chronicle as markdown ready to write to disk. */
40
+ export declare function renderChronicle(chronicle: Chronicle): string;
41
+ //# sourceMappingURL=chronicle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronicle.d.ts","sourceRoot":"","sources":["../../src/insights/chronicle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAeD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1D,SAAS,CAgEX;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CA4B5D"}
@@ -0,0 +1,169 @@
1
+ const TITLES = [
2
+ "The Founding",
3
+ "The First Expansion",
4
+ "The Auth Wars",
5
+ "The Great Refactor",
6
+ "The Quiet Year",
7
+ "The Reckoning",
8
+ "The New Era",
9
+ "The Revival",
10
+ "The Architecture Schism",
11
+ "The Stabilization",
12
+ ];
13
+ /**
14
+ * Build chapters from commit history. We split history into epochs
15
+ * detected by long gaps OR by sudden churn shifts (rewrite events).
16
+ */
17
+ export function buildChronicle(commits, opts = {}) {
18
+ const gapDays = opts.gapDays ?? 30;
19
+ const minChapter = opts.minChapterCommits ?? 5;
20
+ const sorted = [...commits].sort((a, b) => a.authorDate.localeCompare(b.authorDate));
21
+ if (sorted.length === 0) {
22
+ return { totalCommits: 0, totalDays: 0, chapters: [] };
23
+ }
24
+ // First pass: split by long gaps
25
+ const groups = [];
26
+ let current = [sorted[0]];
27
+ for (let i = 1; i < sorted.length; i++) {
28
+ const prev = sorted[i - 1];
29
+ const cur = sorted[i];
30
+ const gap = (new Date(cur.authorDate).getTime() - new Date(prev.authorDate).getTime()) / 86_400_000;
31
+ if (gap >= gapDays && current.length >= minChapter) {
32
+ groups.push(current);
33
+ current = [cur];
34
+ }
35
+ else {
36
+ current.push(cur);
37
+ }
38
+ }
39
+ groups.push(current);
40
+ // Build chapters
41
+ const chapters = [];
42
+ for (let i = 0; i < groups.length; i++) {
43
+ const g = groups[i];
44
+ if (g.length === 0)
45
+ continue;
46
+ const fromDate = g[0].authorDate.slice(0, 10);
47
+ const toDate = g[g.length - 1].authorDate.slice(0, 10);
48
+ const spanDays = Math.max(0, Math.round((new Date(toDate).getTime() - new Date(fromDate).getTime()) / 86_400_000));
49
+ chapters.push({
50
+ number: i + 1,
51
+ title: titleFor(i, g),
52
+ subtitle: subtitleFor(g),
53
+ fromDate,
54
+ toDate,
55
+ spanDays,
56
+ commits: g,
57
+ protagonist: protagonistOf(g),
58
+ opening: openingOf(g),
59
+ closing: closingOf(g),
60
+ });
61
+ }
62
+ const totalDays = Math.round((new Date(sorted[sorted.length - 1].authorDate).getTime() -
63
+ new Date(sorted[0].authorDate).getTime()) /
64
+ 86_400_000);
65
+ return {
66
+ totalCommits: sorted.length,
67
+ totalDays,
68
+ chapters,
69
+ };
70
+ }
71
+ /** Render chronicle as markdown ready to write to disk. */
72
+ export function renderChronicle(chronicle) {
73
+ if (chronicle.chapters.length === 0) {
74
+ return "# Codebase Chronicles\n\n_No commits to chronicle yet._\n";
75
+ }
76
+ const lines = [];
77
+ lines.push("# Chronicles of Your Codebase");
78
+ lines.push("");
79
+ lines.push(`> ${chronicle.totalCommits} commits across ${chronicle.totalDays} days, told in ${chronicle.chapters.length} chapters.`);
80
+ lines.push("");
81
+ lines.push("---");
82
+ lines.push("");
83
+ for (const ch of chronicle.chapters) {
84
+ lines.push(`## Chapter ${ch.number} · ${ch.title}`);
85
+ lines.push("");
86
+ lines.push(`*${ch.subtitle}*`);
87
+ lines.push("");
88
+ lines.push(`**${ch.fromDate} → ${ch.toDate}** · ${ch.spanDays} days · ${ch.commits.length} commits · protagonist: **@${ch.protagonist}**`);
89
+ lines.push("");
90
+ lines.push(ch.opening);
91
+ lines.push("");
92
+ if (ch.closing && ch.closing !== ch.opening) {
93
+ lines.push(ch.closing);
94
+ lines.push("");
95
+ }
96
+ lines.push("---");
97
+ lines.push("");
98
+ }
99
+ return lines.join("\n");
100
+ }
101
+ function titleFor(idx, commits) {
102
+ if (idx === 0)
103
+ return "The Founding";
104
+ // detect refactor cluster
105
+ const text = commits
106
+ .map((c) => c.subject + " " + (c.body || ""))
107
+ .join(" ")
108
+ .toLowerCase();
109
+ if (/\b(rewrite|refactor|migrate|overhaul)\b/.test(text)) {
110
+ return "The Great Refactor";
111
+ }
112
+ if (/\b(hotfix|incident|outage|critical|emergency)\b/.test(text)) {
113
+ return "The Reckoning";
114
+ }
115
+ if (commits.length < 8) {
116
+ return "The Quiet Stretch";
117
+ }
118
+ return TITLES[Math.min(idx, TITLES.length - 1)] ?? `Era ${idx + 1}`;
119
+ }
120
+ function subtitleFor(commits) {
121
+ // Prefer the longest subject from the first 5 commits as flavor
122
+ const top = commits
123
+ .slice(0, 5)
124
+ .reduce((best, c) => (c.subject.length > best.subject.length ? c : best), commits[0]);
125
+ return truncate(top.subject, 100);
126
+ }
127
+ function protagonistOf(commits) {
128
+ const counts = new Map();
129
+ for (const c of commits) {
130
+ const key = c.authorName || c.authorEmail;
131
+ counts.set(key, (counts.get(key) ?? 0) + 1);
132
+ }
133
+ let best = "unknown";
134
+ let bestN = -1;
135
+ for (const [k, n] of counts) {
136
+ if (n > bestN) {
137
+ bestN = n;
138
+ best = k;
139
+ }
140
+ }
141
+ return best;
142
+ }
143
+ function openingOf(commits) {
144
+ // Use the first commit's subject as the opening sentence
145
+ const first = commits[0];
146
+ const author = first.authorName || first.authorEmail.split("@")[0] || "the team";
147
+ return `On **${first.authorDate.slice(0, 10)}**, ${author} began with: *"${truncate(first.subject, 120)}"*. ${commits.length} commits would follow over ${humanSpan(commits)}.`;
148
+ }
149
+ function closingOf(commits) {
150
+ const last = commits[commits.length - 1];
151
+ return `By **${last.authorDate.slice(0, 10)}**, the chapter closed with *"${truncate(last.subject, 120)}"*.`;
152
+ }
153
+ function humanSpan(commits) {
154
+ if (commits.length < 2)
155
+ return "a single day";
156
+ const first = new Date(commits[0].authorDate).getTime();
157
+ const last = new Date(commits[commits.length - 1].authorDate).getTime();
158
+ const days = Math.max(1, Math.round((last - first) / 86_400_000));
159
+ if (days < 30)
160
+ return `${days} day${days === 1 ? "" : "s"}`;
161
+ if (days < 365)
162
+ return `${Math.round(days / 30)} months`;
163
+ return `${(days / 365).toFixed(1)} years`;
164
+ }
165
+ function truncate(s, n) {
166
+ const oneLine = s.replace(/\s+/g, " ").trim();
167
+ return oneLine.length <= n ? oneLine : oneLine.slice(0, n - 1).trimEnd() + "…";
168
+ }
169
+ //# sourceMappingURL=chronicle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronicle.js","sourceRoot":"","sources":["../../src/insights/chronicle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=chronicle.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronicle.test.d.ts","sourceRoot":"","sources":["../../src/insights/chronicle.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,92 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildChronicle, renderChronicle } from "./chronicle.js";
3
+ function mk(p) {
4
+ return {
5
+ hash: p.hash,
6
+ shortHash: p.hash.slice(0, 7),
7
+ authorName: p.author ?? "Alice",
8
+ authorEmail: (p.author ?? "alice").toLowerCase() + "@example.com",
9
+ authorDate: p.date,
10
+ committerDate: p.date,
11
+ subject: p.subject,
12
+ body: p.body ?? "",
13
+ files: [],
14
+ parents: [],
15
+ };
16
+ }
17
+ describe("buildChronicle", () => {
18
+ it("returns empty chronicle when no commits", () => {
19
+ const c = buildChronicle([]);
20
+ expect(c.totalCommits).toBe(0);
21
+ expect(c.chapters).toHaveLength(0);
22
+ });
23
+ it("creates a single chapter from contiguous commits", () => {
24
+ const commits = Array.from({ length: 8 }, (_, i) => mk({ hash: `c${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: `feat: x${i}` }));
25
+ const c = buildChronicle(commits, { gapDays: 30 });
26
+ expect(c.chapters.length).toBe(1);
27
+ expect(c.chapters[0].commits.length).toBe(8);
28
+ });
29
+ it("splits chapters at long gaps (>= gapDays)", () => {
30
+ const commits = [
31
+ ...Array.from({ length: 6 }, (_, i) => mk({ hash: `a${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: `feat: x${i}` })),
32
+ ...Array.from({ length: 6 }, (_, i) => mk({ hash: `b${i}`, date: `2024-08-${(i + 1).toString().padStart(2, "0")}`, subject: `refactor: y${i}` })),
33
+ ];
34
+ const c = buildChronicle(commits, { gapDays: 30, minChapterCommits: 3 });
35
+ expect(c.chapters.length).toBeGreaterThanOrEqual(2);
36
+ });
37
+ it("first chapter is always titled 'The Founding'", () => {
38
+ const commits = Array.from({ length: 5 }, (_, i) => mk({ hash: `c${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: `init x${i}` }));
39
+ const c = buildChronicle(commits);
40
+ expect(c.chapters[0].title).toBe("The Founding");
41
+ });
42
+ it("titles a refactor-heavy chapter as 'The Great Refactor'", () => {
43
+ const commits = [
44
+ ...Array.from({ length: 5 }, (_, i) => mk({ hash: `a${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: `feat: x${i}` })),
45
+ ...Array.from({ length: 5 }, (_, i) => mk({ hash: `b${i}`, date: `2024-06-${(i + 1).toString().padStart(2, "0")}`, subject: `rewrite parser ${i}` })),
46
+ ];
47
+ const c = buildChronicle(commits, { gapDays: 30, minChapterCommits: 3 });
48
+ const refactorChapter = c.chapters.find((ch) => ch.title === "The Great Refactor");
49
+ expect(refactorChapter).toBeDefined();
50
+ });
51
+ it("protagonist is the most-frequent author of the chapter", () => {
52
+ const commits = [
53
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "Alice" }),
54
+ mk({ hash: "a2", date: "2024-01-02", subject: "x", author: "Alice" }),
55
+ mk({ hash: "b1", date: "2024-01-03", subject: "x", author: "Bob" }),
56
+ ];
57
+ const c = buildChronicle(commits);
58
+ expect(c.chapters[0].protagonist).toBe("Alice");
59
+ });
60
+ it("opening references the first commit's subject", () => {
61
+ const commits = [
62
+ mk({ hash: "a1", date: "2024-01-01", subject: "Initial scaffold of payment service" }),
63
+ mk({ hash: "a2", date: "2024-01-02", subject: "x" }),
64
+ ];
65
+ const c = buildChronicle(commits);
66
+ expect(c.chapters[0].opening).toContain("Initial scaffold of payment service");
67
+ });
68
+ it("renderChronicle produces valid markdown with chapter headings", () => {
69
+ const commits = Array.from({ length: 5 }, (_, i) => mk({ hash: `c${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: `feat: x${i}` }));
70
+ const c = buildChronicle(commits);
71
+ const md = renderChronicle(c);
72
+ expect(md).toContain("# Chronicles of Your Codebase");
73
+ expect(md).toMatch(/## Chapter 1 ·/);
74
+ expect(md).toContain("**2024-01-01");
75
+ });
76
+ it("renderChronicle handles empty chronicle gracefully", () => {
77
+ const md = renderChronicle({ totalCommits: 0, totalDays: 0, chapters: [] });
78
+ expect(md).toContain("No commits to chronicle yet");
79
+ });
80
+ it("chapter dates are in chronological order", () => {
81
+ const commits = [
82
+ mk({ hash: "a1", date: "2024-01-01", subject: "x" }),
83
+ mk({ hash: "a2", date: "2024-02-01", subject: "x" }),
84
+ mk({ hash: "a3", date: "2024-03-01", subject: "x" }),
85
+ ];
86
+ const c = buildChronicle(commits, { gapDays: 1, minChapterCommits: 1 });
87
+ for (let i = 1; i < c.chapters.length; i++) {
88
+ expect(c.chapters[i - 1].fromDate.localeCompare(c.chapters[i].fromDate)).toBeLessThanOrEqual(0);
89
+ }
90
+ });
91
+ });
92
+ //# sourceMappingURL=chronicle.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronicle.test.js","sourceRoot":"","sources":["../../src/insights/chronicle.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `mneme cluster` — semantic clustering of commit messages.
3
+ *
4
+ * Closes the "commit message NLP clustering" gap from the landscape
5
+ * survey: academic papers (arxiv 2110.00697) cluster commits semantically
6
+ * but never ship. Mneme ships it.
7
+ *
8
+ * Design choices:
9
+ * - We DON'T ship a heavyweight embedding pipeline here. Instead we
10
+ * use lightweight token shingles + Jaccard similarity, which gives
11
+ * ~80% of the value at near-zero cost and zero deps. When commits
12
+ * have embeddings indexed (via @mneme-ai/embeddings), the caller
13
+ * can pass them in and we'll use cosine instead.
14
+ * - Clustering is single-pass agglomerative — for a few thousand
15
+ * commits it's fast enough; for 100k commits the caller should
16
+ * pre-filter by date / path.
17
+ * - Cluster topics are extracted by surfacing the top-N tokens that
18
+ * appear in ≥ K of a cluster's commit subjects (cluster-defining
19
+ * vocabulary).
20
+ *
21
+ * Pure data extraction. CLI renders.
22
+ */
23
+ import type { Commit } from "../types.js";
24
+ export interface CommitCluster {
25
+ id: number;
26
+ /** Number of commits in this cluster. */
27
+ size: number;
28
+ /** Top vocabulary terms shared across the cluster's commit subjects. */
29
+ topTerms: string[];
30
+ /** Sample commits (most representative — closest to the cluster centroid). */
31
+ samples: Commit[];
32
+ /** Date range covered by this cluster. */
33
+ fromDate: string;
34
+ toDate: string;
35
+ /** Cluster cohesion 0..1 — average pairwise similarity. */
36
+ cohesion: number;
37
+ }
38
+ export interface ClusterReport {
39
+ /** Total commits considered. */
40
+ totalCommits: number;
41
+ /** Resulting clusters, sorted by size descending. */
42
+ clusters: CommitCluster[];
43
+ /** Commits that didn't fit any cluster (singletons). */
44
+ outliers: Commit[];
45
+ }
46
+ export declare function buildClusters(commits: Commit[], opts?: {
47
+ similarityFloor?: number;
48
+ minClusterSize?: number;
49
+ maxSamplesPerCluster?: number;
50
+ embeddings?: Map<string, Float32Array>;
51
+ }): ClusterReport;
52
+ //# sourceMappingURL=cluster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/insights/cluster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAUD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE;IACJ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACnC,GACL,aAAa,CAmEf"}
@@ -0,0 +1,154 @@
1
+ const STOPWORDS = new Set([
2
+ "the", "a", "an", "of", "to", "in", "on", "for", "and", "or", "with", "add", "added", "fix",
3
+ "remove", "update", "change", "make", "made", "new", "old", "this", "that", "is", "are", "be",
4
+ "was", "were", "my", "our", "your", "i", "we", "you", "it", "its", "into", "from", "by", "at",
5
+ "feat", "chore", "docs", "test", "refactor", "style", "build", "ci", "perf", "revert",
6
+ "merge", "branch", "pr", "issue", "close", "closes", "fixes",
7
+ ]);
8
+ export function buildClusters(commits, opts = {}) {
9
+ const floor = opts.similarityFloor ?? 0.3;
10
+ const minSize = opts.minClusterSize ?? 2;
11
+ const maxSamples = opts.maxSamplesPerCluster ?? 3;
12
+ if (commits.length === 0) {
13
+ return { totalCommits: 0, clusters: [], outliers: [] };
14
+ }
15
+ const useEmbeddings = (opts.embeddings?.size ?? 0) > 0;
16
+ // Pre-tokenize each commit for cheap reuse
17
+ const tokenized = commits.map((c) => ({
18
+ commit: c,
19
+ tokens: tokenizeSet(`${c.subject}\n${c.body || ""}`),
20
+ vec: opts.embeddings?.get(c.hash),
21
+ }));
22
+ // Greedy single-pass: each commit joins the first cluster that exceeds floor
23
+ const clusters = [];
24
+ for (const item of tokenized) {
25
+ let joined = false;
26
+ for (const cluster of clusters) {
27
+ const sample = cluster[0];
28
+ const sim = useEmbeddings && sample.vec && item.vec
29
+ ? cosine(sample.vec, item.vec)
30
+ : jaccard(sample.tokens, item.tokens);
31
+ if (sim >= floor) {
32
+ cluster.push(item);
33
+ joined = true;
34
+ break;
35
+ }
36
+ }
37
+ if (!joined)
38
+ clusters.push([item]);
39
+ }
40
+ // Build report
41
+ const result = [];
42
+ const outliers = [];
43
+ let id = 1;
44
+ for (const cluster of clusters) {
45
+ if (cluster.length < minSize) {
46
+ for (const it of cluster)
47
+ outliers.push(it.commit);
48
+ continue;
49
+ }
50
+ const allCommits = cluster.map((c) => c.commit).sort((a, b) => a.authorDate.localeCompare(b.authorDate));
51
+ result.push({
52
+ id: id++,
53
+ size: cluster.length,
54
+ topTerms: clusterTopTerms(cluster.map((c) => c.tokens)),
55
+ samples: pickSamples(cluster, maxSamples),
56
+ fromDate: allCommits[0].authorDate.slice(0, 10),
57
+ toDate: allCommits[allCommits.length - 1].authorDate.slice(0, 10),
58
+ cohesion: avgCohesion(cluster, useEmbeddings),
59
+ });
60
+ }
61
+ result.sort((a, b) => b.size - a.size);
62
+ return {
63
+ totalCommits: commits.length,
64
+ clusters: result,
65
+ outliers,
66
+ };
67
+ }
68
+ function tokenizeSet(text) {
69
+ const out = new Set();
70
+ for (const t of text
71
+ .toLowerCase()
72
+ .replace(/[^a-z0-9_/.-]+/g, " ")
73
+ .split(/\s+/)) {
74
+ if (!t || t.length < 3)
75
+ continue;
76
+ if (STOPWORDS.has(t))
77
+ continue;
78
+ out.add(t);
79
+ }
80
+ return out;
81
+ }
82
+ function jaccard(a, b) {
83
+ if (a.size === 0 && b.size === 0)
84
+ return 1;
85
+ let inter = 0;
86
+ for (const x of a)
87
+ if (b.has(x))
88
+ inter++;
89
+ const union = a.size + b.size - inter;
90
+ return union === 0 ? 0 : inter / union;
91
+ }
92
+ function cosine(a, b) {
93
+ let dot = 0, na = 0, nb = 0;
94
+ const n = Math.min(a.length, b.length);
95
+ for (let i = 0; i < n; i++) {
96
+ const ai = a[i] ?? 0;
97
+ const bi = b[i] ?? 0;
98
+ dot += ai * bi;
99
+ na += ai * ai;
100
+ nb += bi * bi;
101
+ }
102
+ const denom = Math.sqrt(na) * Math.sqrt(nb);
103
+ return denom === 0 ? 0 : dot / denom;
104
+ }
105
+ function clusterTopTerms(tokenSets) {
106
+ if (tokenSets.length === 0)
107
+ return [];
108
+ const docFreq = new Map();
109
+ for (const ts of tokenSets) {
110
+ for (const t of ts)
111
+ docFreq.set(t, (docFreq.get(t) ?? 0) + 1);
112
+ }
113
+ const minDocsForTerm = Math.max(2, Math.ceil(tokenSets.length / 3));
114
+ return [...docFreq.entries()]
115
+ .filter(([, count]) => count >= minDocsForTerm)
116
+ .sort((a, b) => b[1] - a[1])
117
+ .slice(0, 5)
118
+ .map(([term]) => term);
119
+ }
120
+ function pickSamples(items, k) {
121
+ if (items.length <= k)
122
+ return items.map((i) => i.commit);
123
+ // Closest to centroid (by avg jaccard to others)
124
+ const scored = items.map((it) => {
125
+ let total = 0;
126
+ for (const other of items) {
127
+ if (other === it)
128
+ continue;
129
+ total += jaccard(it.tokens, other.tokens);
130
+ }
131
+ return { it, score: total };
132
+ });
133
+ scored.sort((a, b) => b.score - a.score);
134
+ return scored.slice(0, k).map((s) => s.it.commit);
135
+ }
136
+ function avgCohesion(items, useEmbeddings) {
137
+ if (items.length < 2)
138
+ return 1;
139
+ let sum = 0;
140
+ let pairs = 0;
141
+ for (let i = 0; i < items.length; i++) {
142
+ for (let j = i + 1; j < items.length; j++) {
143
+ const a = items[i];
144
+ const b = items[j];
145
+ const sim = useEmbeddings && a.vec && b.vec
146
+ ? cosine(a.vec, b.vec)
147
+ : jaccard(a.tokens, b.tokens);
148
+ sum += sim;
149
+ pairs++;
150
+ }
151
+ }
152
+ return pairs === 0 ? 0 : Number((sum / pairs).toFixed(3));
153
+ }
154
+ //# sourceMappingURL=cluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.js","sourceRoot":"","sources":["../../src/insights/cluster.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cluster.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.test.d.ts","sourceRoot":"","sources":["../../src/insights/cluster.test.ts"],"names":[],"mappings":""}