@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,106 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildClusters } from "./cluster.js";
3
+ function mk(p) {
4
+ return {
5
+ hash: p.hash,
6
+ shortHash: p.hash.slice(0, 7),
7
+ authorName: "Test",
8
+ authorEmail: "t@e.com",
9
+ authorDate: p.date ?? "2024-01-01",
10
+ committerDate: p.date ?? "2024-01-01",
11
+ subject: p.subject,
12
+ body: p.body ?? "",
13
+ files: [],
14
+ parents: [],
15
+ };
16
+ }
17
+ describe("buildClusters", () => {
18
+ it("returns empty when no commits", () => {
19
+ const r = buildClusters([]);
20
+ expect(r.totalCommits).toBe(0);
21
+ expect(r.clusters).toHaveLength(0);
22
+ });
23
+ it("groups similar commits into one cluster", () => {
24
+ const commits = [
25
+ mk({ hash: "a1", subject: "add caching layer to api responses" }),
26
+ mk({ hash: "a2", subject: "extend caching to user objects" }),
27
+ mk({ hash: "a3", subject: "improve caching hit ratio" }),
28
+ mk({ hash: "b1", subject: "fix typo in README" }),
29
+ mk({ hash: "b2", subject: "fix typo in CHANGELOG" }),
30
+ ];
31
+ const r = buildClusters(commits, { similarityFloor: 0.1, minClusterSize: 2 });
32
+ const cacheCluster = r.clusters.find((c) => c.topTerms.includes("caching"));
33
+ expect(cacheCluster).toBeDefined();
34
+ expect(cacheCluster.size).toBeGreaterThanOrEqual(2);
35
+ });
36
+ it("singletons go to outliers when minClusterSize >= 2", () => {
37
+ const commits = [
38
+ mk({ hash: "a1", subject: "completely unique subject xyz" }),
39
+ mk({ hash: "a2", subject: "another distinct subject abc" }),
40
+ ];
41
+ const r = buildClusters(commits, { similarityFloor: 0.5, minClusterSize: 2 });
42
+ expect(r.outliers.length).toBe(2);
43
+ });
44
+ it("clusters are sorted by size descending", () => {
45
+ const commits = [
46
+ mk({ hash: "a1", subject: "add caching layer" }),
47
+ mk({ hash: "a2", subject: "add caching to api" }),
48
+ mk({ hash: "a3", subject: "add caching index" }),
49
+ mk({ hash: "b1", subject: "fix bug in handler" }),
50
+ mk({ hash: "b2", subject: "fix bug in router" }),
51
+ ];
52
+ const r = buildClusters(commits, { similarityFloor: 0.2, minClusterSize: 2 });
53
+ for (let i = 1; i < r.clusters.length; i++) {
54
+ expect(r.clusters[i - 1].size).toBeGreaterThanOrEqual(r.clusters[i].size);
55
+ }
56
+ });
57
+ it("topTerms reflect shared cluster vocabulary", () => {
58
+ const commits = [
59
+ mk({ hash: "a1", subject: "rewrite authentication middleware" }),
60
+ mk({ hash: "a2", subject: "rewrite authentication tests" }),
61
+ mk({ hash: "a3", subject: "polish authentication" }),
62
+ ];
63
+ const r = buildClusters(commits, { similarityFloor: 0.1, minClusterSize: 2 });
64
+ const auth = r.clusters[0];
65
+ expect(auth).toBeDefined();
66
+ expect(auth.topTerms).toContain("authentication");
67
+ });
68
+ it("samples never exceed maxSamplesPerCluster", () => {
69
+ const commits = Array.from({ length: 8 }, (_, i) => mk({ hash: `a${i}`, subject: `caching ${i} layer api` }));
70
+ const r = buildClusters(commits, { similarityFloor: 0.15, minClusterSize: 2, maxSamplesPerCluster: 3 });
71
+ for (const c of r.clusters) {
72
+ expect(c.samples.length).toBeLessThanOrEqual(3);
73
+ }
74
+ });
75
+ it("cohesion of single-commit cluster is 1", () => {
76
+ const commits = [mk({ hash: "a1", subject: "lonely commit subject" })];
77
+ const r = buildClusters(commits, { similarityFloor: 0, minClusterSize: 1 });
78
+ if (r.clusters.length > 0) {
79
+ expect(r.clusters[0].cohesion).toBe(1);
80
+ }
81
+ });
82
+ it("date range covers earliest and latest commit in cluster", () => {
83
+ const commits = [
84
+ mk({ hash: "a1", subject: "add caching layer", date: "2024-01-01" }),
85
+ mk({ hash: "a2", subject: "add caching tests", date: "2024-06-15" }),
86
+ ];
87
+ const r = buildClusters(commits, { similarityFloor: 0.1, minClusterSize: 2 });
88
+ const c = r.clusters[0];
89
+ if (c) {
90
+ expect(c.fromDate).toBe("2024-01-01");
91
+ expect(c.toDate).toBe("2024-06-15");
92
+ }
93
+ });
94
+ it("respects similarityFloor — high floor produces more clusters", () => {
95
+ const commits = [
96
+ mk({ hash: "a1", subject: "add caching" }),
97
+ mk({ hash: "a2", subject: "add cache" }),
98
+ mk({ hash: "a3", subject: "add caching layer" }),
99
+ ];
100
+ const lowFloor = buildClusters(commits, { similarityFloor: 0.05, minClusterSize: 2 });
101
+ const highFloor = buildClusters(commits, { similarityFloor: 0.95, minClusterSize: 2 });
102
+ // High floor splits more aggressively → fewer joins → more outliers
103
+ expect(highFloor.outliers.length).toBeGreaterThanOrEqual(lowFloor.outliers.length);
104
+ });
105
+ });
106
+ //# sourceMappingURL=cluster.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.test.js","sourceRoot":"","sources":["../../src/insights/cluster.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `mneme constellation` — build a graph view of the codebase as a living
3
+ * map: files are stars, authors are orbital bodies, edges are co-edits
4
+ * (file↔file) and authorships (file↔author).
5
+ *
6
+ * v0.12 ships the graph extraction + JSON export + ASCII summary. A web
7
+ * UI (`mneme constellation --serve` for a 3D WebGL viewer) is on the
8
+ * roadmap; the graph data is the underlying truth either way.
9
+ *
10
+ * Pure data extraction. No external services. No 3D rendering here —
11
+ * just the graph the renderer needs.
12
+ */
13
+ import type { Commit } from "../types.js";
14
+ export interface FileStar {
15
+ filePath: string;
16
+ /** Total times the file was committed in window. */
17
+ weight: number;
18
+ /** Last touched ISO date. */
19
+ lastTouched: string;
20
+ /** Top-level directory — useful for clustering / coloring. */
21
+ cluster: string;
22
+ }
23
+ export interface AuthorOrbital {
24
+ author: string;
25
+ /** Total commits in window. */
26
+ weight: number;
27
+ /** Top files this author "orbits" (by touch share). */
28
+ topFiles: string[];
29
+ }
30
+ export interface FileEdge {
31
+ /** Sorted alphabetically: a < b. */
32
+ fileA: string;
33
+ fileB: string;
34
+ /** Number of commits that touched both files together. */
35
+ coEdits: number;
36
+ }
37
+ export interface AuthorshipEdge {
38
+ author: string;
39
+ filePath: string;
40
+ touches: number;
41
+ }
42
+ export interface Constellation {
43
+ windowCommits: number;
44
+ fileStars: FileStar[];
45
+ authorOrbitals: AuthorOrbital[];
46
+ fileEdges: FileEdge[];
47
+ authorshipEdges: AuthorshipEdge[];
48
+ /** Number of distinct top-level clusters (directories) detected. */
49
+ clusterCount: number;
50
+ }
51
+ export declare function buildConstellation(commits: Commit[], opts?: {
52
+ minFileTouches?: number;
53
+ minAuthorCommits?: number;
54
+ maxFileEdges?: number;
55
+ maxStars?: number;
56
+ maxOrbitals?: number;
57
+ }): Constellation;
58
+ /**
59
+ * Render an ASCII summary suitable for terminal output. The web UI will
60
+ * consume the JSON directly via `--json`.
61
+ */
62
+ export declare function renderConstellationAscii(c: Constellation, topN?: number): string;
63
+ //# sourceMappingURL=constellation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constellation.d.ts","sourceRoot":"","sources":["../../src/insights/constellation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE;IACJ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,GACL,aAAa,CAgHf;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,SAAI,GAAG,MAAM,CAsB3E"}
@@ -0,0 +1,147 @@
1
+ export function buildConstellation(commits, opts = {}) {
2
+ const minFileTouches = opts.minFileTouches ?? 2;
3
+ const minAuthorCommits = opts.minAuthorCommits ?? 2;
4
+ const maxFileEdges = opts.maxFileEdges ?? 200;
5
+ const maxStars = opts.maxStars ?? 200;
6
+ const maxOrbitals = opts.maxOrbitals ?? 30;
7
+ // file → { weight, lastTouched }
8
+ const filesMap = new Map();
9
+ // author → { weight, topFiles map }
10
+ const authorsMap = new Map();
11
+ // co-edit map: "a||b" → coEdits
12
+ const coEditMap = new Map();
13
+ // authorship: "author||file" → touches
14
+ const authorshipMap = new Map();
15
+ for (const c of commits) {
16
+ const files = c.files;
17
+ const author = c.authorName || c.authorEmail;
18
+ // bump file weight
19
+ for (const f of files) {
20
+ let fr = filesMap.get(f);
21
+ if (!fr) {
22
+ fr = { weight: 0, lastTouched: c.authorDate };
23
+ filesMap.set(f, fr);
24
+ }
25
+ fr.weight += 1;
26
+ if (c.authorDate.localeCompare(fr.lastTouched) > 0) {
27
+ fr.lastTouched = c.authorDate;
28
+ }
29
+ // authorship edge
30
+ const akey = `${author}||${f}`;
31
+ let ae = authorshipMap.get(akey);
32
+ if (!ae) {
33
+ ae = { author, filePath: f, touches: 0 };
34
+ authorshipMap.set(akey, ae);
35
+ }
36
+ ae.touches += 1;
37
+ // author orbital weight
38
+ let ar = authorsMap.get(author);
39
+ if (!ar) {
40
+ ar = { weight: 0, files: new Map() };
41
+ authorsMap.set(author, ar);
42
+ }
43
+ ar.weight += 1;
44
+ ar.files.set(f, (ar.files.get(f) ?? 0) + 1);
45
+ }
46
+ // co-edit edges (every pair of files in a commit)
47
+ for (let i = 0; i < files.length; i++) {
48
+ for (let j = i + 1; j < files.length; j++) {
49
+ const a = files[i];
50
+ const b = files[j];
51
+ const [x, y] = a < b ? [a, b] : [b, a];
52
+ const key = `${x}||${y}`;
53
+ coEditMap.set(key, (coEditMap.get(key) ?? 0) + 1);
54
+ }
55
+ }
56
+ }
57
+ // Build file stars (filtered + capped)
58
+ const fileStars = [];
59
+ for (const [filePath, info] of filesMap) {
60
+ if (info.weight < minFileTouches)
61
+ continue;
62
+ fileStars.push({
63
+ filePath,
64
+ weight: info.weight,
65
+ lastTouched: info.lastTouched.slice(0, 10),
66
+ cluster: filePath.split("/").slice(0, 2).join("/") || "(root)",
67
+ });
68
+ }
69
+ fileStars.sort((a, b) => b.weight - a.weight);
70
+ if (fileStars.length > maxStars)
71
+ fileStars.length = maxStars;
72
+ // Build orbitals (filtered + capped)
73
+ const authorOrbitals = [];
74
+ for (const [author, info] of authorsMap) {
75
+ if (info.weight < minAuthorCommits)
76
+ continue;
77
+ const topFiles = [...info.files.entries()]
78
+ .sort((a, b) => b[1] - a[1])
79
+ .slice(0, 5)
80
+ .map(([f]) => f);
81
+ authorOrbitals.push({ author, weight: info.weight, topFiles });
82
+ }
83
+ authorOrbitals.sort((a, b) => b.weight - a.weight);
84
+ if (authorOrbitals.length > maxOrbitals)
85
+ authorOrbitals.length = maxOrbitals;
86
+ // Build file edges (top by coEdits)
87
+ const allFileEdges = [];
88
+ for (const [key, n] of coEditMap) {
89
+ if (n < 2)
90
+ continue;
91
+ const [a, b] = key.split("||");
92
+ allFileEdges.push({ fileA: a, fileB: b, coEdits: n });
93
+ }
94
+ allFileEdges.sort((a, b) => b.coEdits - a.coEdits);
95
+ const fileEdges = allFileEdges.slice(0, maxFileEdges);
96
+ // Build authorship edges
97
+ const authorshipEdges = [...authorshipMap.values()]
98
+ .filter((e) => e.touches >= minFileTouches)
99
+ .sort((a, b) => b.touches - a.touches);
100
+ // Cluster count
101
+ const clusters = new Set(fileStars.map((s) => s.cluster));
102
+ return {
103
+ windowCommits: commits.length,
104
+ fileStars,
105
+ authorOrbitals,
106
+ fileEdges,
107
+ authorshipEdges,
108
+ clusterCount: clusters.size,
109
+ };
110
+ }
111
+ /**
112
+ * Render an ASCII summary suitable for terminal output. The web UI will
113
+ * consume the JSON directly via `--json`.
114
+ */
115
+ export function renderConstellationAscii(c, topN = 8) {
116
+ const lines = [];
117
+ lines.push("");
118
+ lines.push(` ${c.fileStars.length} file-stars · ${c.authorOrbitals.length} orbitals · ${c.fileEdges.length} co-edit edges · ${c.clusterCount} clusters`);
119
+ lines.push("");
120
+ lines.push(" ◆ Brightest stars (most-touched files)");
121
+ for (const s of c.fileStars.slice(0, topN)) {
122
+ const meter = bar(s.weight, c.fileStars[0]?.weight ?? 1);
123
+ lines.push(` ${meter} ${s.filePath} (${s.weight}×)`);
124
+ }
125
+ lines.push("");
126
+ lines.push(" ◆ Closest orbitals (most-active authors)");
127
+ for (const o of c.authorOrbitals.slice(0, topN)) {
128
+ const meter = bar(o.weight, c.authorOrbitals[0]?.weight ?? 1);
129
+ lines.push(` ${meter} ${o.author} (${o.weight} commits)`);
130
+ }
131
+ lines.push("");
132
+ lines.push(" ◆ Strongest co-edit edges (files often committed together)");
133
+ for (const e of c.fileEdges.slice(0, topN)) {
134
+ lines.push(` ${e.coEdits}× ${shorten(e.fileA, 35)} ⟷ ${shorten(e.fileB, 35)}`);
135
+ }
136
+ return lines.join("\n");
137
+ }
138
+ function bar(value, max, width = 8) {
139
+ if (max <= 0)
140
+ return " ".repeat(width);
141
+ const blocks = Math.max(0, Math.min(width, Math.round((value / max) * width)));
142
+ return "█".repeat(blocks) + "░".repeat(width - blocks);
143
+ }
144
+ function shorten(s, n) {
145
+ return s.length <= n ? s : "…" + s.slice(s.length - n + 1);
146
+ }
147
+ //# sourceMappingURL=constellation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constellation.js","sourceRoot":"","sources":["../../src/insights/constellation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=constellation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constellation.test.d.ts","sourceRoot":"","sources":["../../src/insights/constellation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildConstellation, renderConstellationAscii } from "./constellation.js";
3
+ function mk(p) {
4
+ return {
5
+ hash: p.hash,
6
+ shortHash: p.hash.slice(0, 7),
7
+ authorName: p.author,
8
+ authorEmail: p.author.toLowerCase() + "@x.com",
9
+ authorDate: p.date,
10
+ committerDate: p.date,
11
+ subject: p.subject,
12
+ body: "",
13
+ files: p.files ?? [],
14
+ parents: [],
15
+ };
16
+ }
17
+ describe("buildConstellation", () => {
18
+ it("returns empty constellation for no commits", () => {
19
+ const c = buildConstellation([]);
20
+ expect(c.fileStars).toHaveLength(0);
21
+ expect(c.authorOrbitals).toHaveLength(0);
22
+ expect(c.fileEdges).toHaveLength(0);
23
+ });
24
+ it("builds file stars sorted by weight descending", () => {
25
+ const commits = [
26
+ ...Array.from({ length: 5 }, (_, i) => mk({ hash: `a${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: "x", author: "Alice", files: ["src/hot.ts"] })),
27
+ ...Array.from({ length: 2 }, (_, i) => mk({ hash: `b${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: "x", author: "Alice", files: ["src/cool.ts"] })),
28
+ ];
29
+ const c = buildConstellation(commits);
30
+ expect(c.fileStars[0].filePath).toBe("src/hot.ts");
31
+ expect(c.fileStars[0].weight).toBe(5);
32
+ });
33
+ it("filters out files below minFileTouches", () => {
34
+ const commits = [
35
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "Alice", files: ["src/rare.ts"] }),
36
+ ];
37
+ const c = buildConstellation(commits, { minFileTouches: 2 });
38
+ expect(c.fileStars).toHaveLength(0);
39
+ });
40
+ it("builds author orbitals sorted by weight desc", () => {
41
+ const commits = [
42
+ ...Array.from({ length: 5 }, (_, i) => mk({ hash: `a${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: "x", author: "Alice", files: ["src/x.ts"] })),
43
+ ...Array.from({ length: 3 }, (_, i) => mk({ hash: `b${i}`, date: `2024-01-${(i + 1).toString().padStart(2, "0")}`, subject: "x", author: "Bob", files: ["src/x.ts"] })),
44
+ ];
45
+ const c = buildConstellation(commits);
46
+ expect(c.authorOrbitals[0].author).toBe("Alice");
47
+ expect(c.authorOrbitals[0].weight).toBe(5);
48
+ });
49
+ it("creates co-edit edges between files committed together", () => {
50
+ const commits = [
51
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "Alice", files: ["src/a.ts", "src/b.ts"] }),
52
+ mk({ hash: "a2", date: "2024-01-02", subject: "x", author: "Alice", files: ["src/a.ts", "src/b.ts"] }),
53
+ ];
54
+ const c = buildConstellation(commits);
55
+ const edge = c.fileEdges.find((e) => (e.fileA === "src/a.ts" && e.fileB === "src/b.ts") || (e.fileA === "src/b.ts" && e.fileB === "src/a.ts"));
56
+ expect(edge).toBeDefined();
57
+ expect(edge.coEdits).toBe(2);
58
+ });
59
+ it("file edges always have fileA < fileB alphabetically", () => {
60
+ const commits = [
61
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "Alice", files: ["src/z.ts", "src/a.ts"] }),
62
+ mk({ hash: "a2", date: "2024-01-02", subject: "x", author: "Alice", files: ["src/z.ts", "src/a.ts"] }),
63
+ ];
64
+ const c = buildConstellation(commits);
65
+ for (const e of c.fileEdges) {
66
+ expect(e.fileA.localeCompare(e.fileB)).toBeLessThan(0);
67
+ }
68
+ });
69
+ it("clusterCount counts distinct top-level directories", () => {
70
+ const commits = [
71
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "A", files: ["src/auth/x.ts", "src/auth/y.ts"] }),
72
+ mk({ hash: "a2", date: "2024-01-02", subject: "x", author: "A", files: ["src/auth/x.ts"] }),
73
+ mk({ hash: "b1", date: "2024-01-03", subject: "x", author: "B", files: ["packages/x.ts"] }),
74
+ mk({ hash: "b2", date: "2024-01-04", subject: "x", author: "B", files: ["packages/x.ts"] }),
75
+ ];
76
+ const c = buildConstellation(commits);
77
+ expect(c.clusterCount).toBeGreaterThanOrEqual(2);
78
+ });
79
+ it("renderConstellationAscii produces non-empty string with key sections", () => {
80
+ const commits = [
81
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "Alice", files: ["src/a.ts", "src/b.ts"] }),
82
+ mk({ hash: "a2", date: "2024-01-02", subject: "x", author: "Alice", files: ["src/a.ts", "src/b.ts"] }),
83
+ ];
84
+ const c = buildConstellation(commits);
85
+ const ascii = renderConstellationAscii(c);
86
+ expect(ascii).toContain("Brightest stars");
87
+ expect(ascii).toContain("Closest orbitals");
88
+ expect(ascii).toContain("co-edit");
89
+ });
90
+ it("respects maxStars cap", () => {
91
+ const commits = [];
92
+ for (let i = 0; i < 50; i++) {
93
+ commits.push(mk({
94
+ hash: `c${i}`,
95
+ date: `2024-01-${((i % 28) + 1).toString().padStart(2, "0")}`,
96
+ subject: "x",
97
+ author: "A",
98
+ files: [`src/f${i}.ts`, `src/f${i}.ts`], // touched twice so it survives min filter
99
+ }));
100
+ }
101
+ const c = buildConstellation(commits, { maxStars: 10 });
102
+ expect(c.fileStars.length).toBeLessThanOrEqual(10);
103
+ });
104
+ });
105
+ //# sourceMappingURL=constellation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constellation.test.js","sourceRoot":"","sources":["../../src/insights/constellation.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * `mneme dna [@author]` — extract a "Codebase DNA" fingerprint for a
3
+ * contributor: their preferred patterns, message style, hours, and file
4
+ * affinity, packaged as an exportable JSON artifact.
5
+ *
6
+ * What makes this novel:
7
+ * - Existing tools either show *snapshot* metrics (HowYouCode) or
8
+ * ownership / hotspots (Hercules). None extract a stable, exportable,
9
+ * comparable per-developer fingerprint from commit *history*.
10
+ * - DNA is shareable as JSON — collaborators can compare strands,
11
+ * teams can baseline, the same person can track themselves over time.
12
+ * - Compatibility scoring (Jaccard over multiple feature axes) gives
13
+ * "how genetically similar are two coders?" in one number.
14
+ *
15
+ * Pure data extraction. CLI renders + writes JSON.
16
+ */
17
+ import type { Commit } from "../types.js";
18
+ export interface CodebaseDna {
19
+ /** Author email or login this strand was extracted from. */
20
+ author: string;
21
+ /** Number of commits used to build this strand. */
22
+ commitCount: number;
23
+ /** Date range covered by this strand. */
24
+ fromDate: string;
25
+ toDate: string;
26
+ /** Style genome — surface-level coding habits inferred from commits. */
27
+ style: StyleGenome;
28
+ /** Commit message DNA — how this person writes. */
29
+ message: MessageGenome;
30
+ /** Working hours pattern (UTC). */
31
+ hours: HoursGenome;
32
+ /** File affinity — what they touch and avoid. */
33
+ files: FilesGenome;
34
+ /**
35
+ * Stable hash of the strand — short identifier suitable for sharing
36
+ * ("DNA hash"), like a git SHA prefix. Computed deterministically from
37
+ * the genome contents.
38
+ */
39
+ hash: string;
40
+ }
41
+ export interface StyleGenome {
42
+ /** Median number of files touched per commit. */
43
+ filesPerCommit: number;
44
+ /** Mean churn per commit (insertions + deletions, capped). */
45
+ churnPerCommit: number;
46
+ /** Fraction of commits touching tests (path includes test/spec). */
47
+ testRatio: number;
48
+ /** Fraction of commits referencing an issue or PR. */
49
+ issueRefRatio: number;
50
+ /** Fraction of commits whose subject starts with a conventional prefix. */
51
+ conventionalRatio: number;
52
+ }
53
+ export interface MessageGenome {
54
+ /** Mean subject length in characters. */
55
+ avgSubjectLength: number;
56
+ /** Fraction of subjects in imperative mood (rough heuristic). */
57
+ imperativeRatio: number;
58
+ /** Fraction of commits that include a body. */
59
+ bodyRatio: number;
60
+ /** Top 5 leading verbs / prefixes used. */
61
+ topVerbs: Array<{
62
+ verb: string;
63
+ count: number;
64
+ }>;
65
+ }
66
+ export interface HoursGenome {
67
+ /** Hour-of-day histogram (UTC). 24 buckets. */
68
+ byHour: number[];
69
+ /** Day-of-week histogram (0=Sunday). 7 buckets. */
70
+ byWeekday: number[];
71
+ /** Fraction of commits on Sat/Sun. */
72
+ weekendRatio: number;
73
+ /** The peak window as "HH:00–HH:00" UTC. */
74
+ peakWindow: string;
75
+ }
76
+ export interface FilesGenome {
77
+ /** Top directories by commit count, normalized. */
78
+ topDirs: Array<{
79
+ dir: string;
80
+ share: number;
81
+ }>;
82
+ /** Top file extensions by commit count. */
83
+ topExts: Array<{
84
+ ext: string;
85
+ share: number;
86
+ }>;
87
+ }
88
+ /** Build a Codebase DNA strand for the given author from commits they made. */
89
+ export declare function extractDna(commits: Commit[], author: string, opts?: {
90
+ now?: number;
91
+ }): CodebaseDna;
92
+ /** Compatibility score 0..1 between two DNA strands. */
93
+ export declare function compareDna(a: CodebaseDna, b: CodebaseDna): {
94
+ similarity: number;
95
+ axes: Array<{
96
+ axis: string;
97
+ similarity: number;
98
+ }>;
99
+ };
100
+ //# sourceMappingURL=dna.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dna.d.ts","sourceRoot":"","sources":["../../src/insights/dna.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,KAAK,EAAE,WAAW,CAAC;IACnB,mDAAmD;IACnD,OAAO,EAAE,aAAa,CAAC;IACvB,mCAAmC;IACnC,KAAK,EAAE,WAAW,CAAC;IACnB,iDAAiD;IACjD,KAAK,EAAE,WAAW,CAAC;IACnB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mDAAmD;IACnD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,2CAA2C;IAC3C,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAYD,+EAA+E;AAC/E,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1B,WAAW,CAkCb;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD,CAYA"}