@mneme-ai/core 0.8.3 → 0.9.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 (144) hide show
  1. package/dist/entities/go-parser.d.ts +47 -0
  2. package/dist/entities/go-parser.d.ts.map +1 -0
  3. package/dist/entities/go-parser.js +315 -0
  4. package/dist/entities/go-parser.js.map +1 -0
  5. package/dist/entities/go-parser.test.d.ts +2 -0
  6. package/dist/entities/go-parser.test.d.ts.map +1 -0
  7. package/dist/entities/go-parser.test.js +147 -0
  8. package/dist/entities/go-parser.test.js.map +1 -0
  9. package/dist/entities/index.d.ts +1 -0
  10. package/dist/entities/index.d.ts.map +1 -1
  11. package/dist/entities/index.js +1 -0
  12. package/dist/entities/index.js.map +1 -1
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/indexer/indexer.d.ts +12 -0
  18. package/dist/indexer/indexer.d.ts.map +1 -1
  19. package/dist/indexer/indexer.js +28 -1
  20. package/dist/indexer/indexer.js.map +1 -1
  21. package/dist/insights/decisions.d.ts +38 -0
  22. package/dist/insights/decisions.d.ts.map +1 -0
  23. package/dist/insights/decisions.js +125 -0
  24. package/dist/insights/decisions.js.map +1 -0
  25. package/dist/insights/decisions.test.d.ts +2 -0
  26. package/dist/insights/decisions.test.d.ts.map +1 -0
  27. package/dist/insights/decisions.test.js +141 -0
  28. package/dist/insights/decisions.test.js.map +1 -0
  29. package/dist/insights/dream.d.ts +71 -0
  30. package/dist/insights/dream.d.ts.map +1 -0
  31. package/dist/insights/dream.js +235 -0
  32. package/dist/insights/dream.js.map +1 -0
  33. package/dist/insights/dream.test.d.ts +2 -0
  34. package/dist/insights/dream.test.d.ts.map +1 -0
  35. package/dist/insights/dream.test.js +127 -0
  36. package/dist/insights/dream.test.js.map +1 -0
  37. package/dist/insights/index.d.ts +16 -0
  38. package/dist/insights/index.d.ts.map +1 -0
  39. package/dist/insights/index.js +16 -0
  40. package/dist/insights/index.js.map +1 -0
  41. package/dist/insights/obsidian.d.ts +42 -0
  42. package/dist/insights/obsidian.d.ts.map +1 -0
  43. package/dist/insights/obsidian.js +263 -0
  44. package/dist/insights/obsidian.js.map +1 -0
  45. package/dist/insights/obsidian.test.d.ts +2 -0
  46. package/dist/insights/obsidian.test.d.ts.map +1 -0
  47. package/dist/insights/obsidian.test.js +241 -0
  48. package/dist/insights/obsidian.test.js.map +1 -0
  49. package/dist/insights/stack-trace.d.ts +40 -0
  50. package/dist/insights/stack-trace.d.ts.map +1 -0
  51. package/dist/insights/stack-trace.js +127 -0
  52. package/dist/insights/stack-trace.js.map +1 -0
  53. package/dist/insights/stack-trace.test.d.ts +2 -0
  54. package/dist/insights/stack-trace.test.d.ts.map +1 -0
  55. package/dist/insights/stack-trace.test.js +103 -0
  56. package/dist/insights/stack-trace.test.js.map +1 -0
  57. package/dist/insights/story.d.ts +34 -0
  58. package/dist/insights/story.d.ts.map +1 -0
  59. package/dist/insights/story.js +100 -0
  60. package/dist/insights/story.js.map +1 -0
  61. package/dist/insights/story.test.d.ts +2 -0
  62. package/dist/insights/story.test.d.ts.map +1 -0
  63. package/dist/insights/story.test.js +99 -0
  64. package/dist/insights/story.test.js.map +1 -0
  65. package/dist/insights/suggest.d.ts +29 -0
  66. package/dist/insights/suggest.d.ts.map +1 -0
  67. package/dist/insights/suggest.js +93 -0
  68. package/dist/insights/suggest.js.map +1 -0
  69. package/dist/insights/suggest.test.d.ts +2 -0
  70. package/dist/insights/suggest.test.d.ts.map +1 -0
  71. package/dist/insights/suggest.test.js +71 -0
  72. package/dist/insights/suggest.test.js.map +1 -0
  73. package/dist/insights/who-knows.d.ts +48 -0
  74. package/dist/insights/who-knows.d.ts.map +1 -0
  75. package/dist/insights/who-knows.js +96 -0
  76. package/dist/insights/who-knows.js.map +1 -0
  77. package/dist/insights/who-knows.test.d.ts +2 -0
  78. package/dist/insights/who-knows.test.d.ts.map +1 -0
  79. package/dist/insights/who-knows.test.js +47 -0
  80. package/dist/insights/who-knows.test.js.map +1 -0
  81. package/dist/retrieve/index.d.ts +2 -0
  82. package/dist/retrieve/index.d.ts.map +1 -1
  83. package/dist/retrieve/index.js +2 -0
  84. package/dist/retrieve/index.js.map +1 -1
  85. package/dist/retrieve/intent.d.ts +32 -0
  86. package/dist/retrieve/intent.d.ts.map +1 -0
  87. package/dist/retrieve/intent.js +104 -0
  88. package/dist/retrieve/intent.js.map +1 -0
  89. package/dist/retrieve/intent.test.d.ts +2 -0
  90. package/dist/retrieve/intent.test.d.ts.map +1 -0
  91. package/dist/retrieve/intent.test.js +106 -0
  92. package/dist/retrieve/intent.test.js.map +1 -0
  93. package/dist/retrieve/search.d.ts +30 -0
  94. package/dist/retrieve/search.d.ts.map +1 -1
  95. package/dist/retrieve/search.js +48 -0
  96. package/dist/retrieve/search.js.map +1 -1
  97. package/dist/retrieve/search.test.js +84 -1
  98. package/dist/retrieve/search.test.js.map +1 -1
  99. package/dist/retrieve/synthesize.d.ts +57 -0
  100. package/dist/retrieve/synthesize.d.ts.map +1 -0
  101. package/dist/retrieve/synthesize.js +160 -0
  102. package/dist/retrieve/synthesize.js.map +1 -0
  103. package/dist/retrieve/synthesize.test.d.ts +2 -0
  104. package/dist/retrieve/synthesize.test.d.ts.map +1 -0
  105. package/dist/retrieve/synthesize.test.js +116 -0
  106. package/dist/retrieve/synthesize.test.js.map +1 -0
  107. package/dist/store/schema.d.ts +2 -2
  108. package/dist/store/schema.d.ts.map +1 -1
  109. package/dist/store/schema.js +55 -1
  110. package/dist/store/schema.js.map +1 -1
  111. package/dist/store/sqlite.test.js +1 -1
  112. package/dist/util/index.d.ts +2 -0
  113. package/dist/util/index.d.ts.map +1 -1
  114. package/dist/util/index.js +2 -1
  115. package/dist/util/index.js.map +1 -1
  116. package/dist/util/redact.d.ts +58 -0
  117. package/dist/util/redact.d.ts.map +1 -0
  118. package/dist/util/redact.js +129 -0
  119. package/dist/util/redact.js.map +1 -0
  120. package/dist/util/redact.test.d.ts +2 -0
  121. package/dist/util/redact.test.d.ts.map +1 -0
  122. package/dist/util/redact.test.js +148 -0
  123. package/dist/util/redact.test.js.map +1 -0
  124. package/dist/wisdom/calibrator.d.ts +43 -0
  125. package/dist/wisdom/calibrator.d.ts.map +1 -0
  126. package/dist/wisdom/calibrator.js +120 -0
  127. package/dist/wisdom/calibrator.js.map +1 -0
  128. package/dist/wisdom/feedback.d.ts +45 -0
  129. package/dist/wisdom/feedback.d.ts.map +1 -0
  130. package/dist/wisdom/feedback.js +116 -0
  131. package/dist/wisdom/feedback.js.map +1 -0
  132. package/dist/wisdom/index.d.ts +15 -0
  133. package/dist/wisdom/index.d.ts.map +1 -0
  134. package/dist/wisdom/index.js +15 -0
  135. package/dist/wisdom/index.js.map +1 -0
  136. package/dist/wisdom/types.d.ts +67 -0
  137. package/dist/wisdom/types.d.ts.map +1 -0
  138. package/dist/wisdom/types.js +20 -0
  139. package/dist/wisdom/types.js.map +1 -0
  140. package/dist/wisdom/wisdom.test.d.ts +2 -0
  141. package/dist/wisdom/wisdom.test.d.ts.map +1 -0
  142. package/dist/wisdom/wisdom.test.js +144 -0
  143. package/dist/wisdom/wisdom.test.js.map +1 -0
  144. package/package.json +1 -1
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Obsidian export — turn Mneme insights into a wiki-linked Markdown vault
3
+ * compatible with Obsidian's [[wiki-links]] + YAML frontmatter conventions.
4
+ *
5
+ * Why this exists: the "LLM Wiki" / "second brain" approach (Karpathy, et al.)
6
+ * pairs personal notes with linked knowledge. Mneme already extracts decisions,
7
+ * stories, and expert maps from git history — exporting them as an Obsidian
8
+ * vault lets users keep their code memory next to their personal notes.
9
+ *
10
+ * Pure functions only. No I/O. The CLI does the file writes.
11
+ *
12
+ * Output shape:
13
+ * vault/
14
+ * index.md ← top-level table
15
+ * decisions/<filename>.md ← one note per decision
16
+ * authors/<name>.md ← per-author backlink hub
17
+ * story/<topic>.md ← per-topic story timeline
18
+ */
19
+ // ─── Decisions → Obsidian ───────────────────────────────────────────────
20
+ export function decisionsToVault(decisions) {
21
+ if (decisions.length === 0) {
22
+ return [
23
+ {
24
+ path: "index.md",
25
+ content: indexHeader() + "\n_No decisions extracted yet._\n",
26
+ },
27
+ ];
28
+ }
29
+ const files = [];
30
+ // 1. One note per decision.
31
+ for (const d of decisions) {
32
+ files.push({
33
+ path: `decisions/${decisionFilename(d)}.md`,
34
+ content: decisionNoteContent(d),
35
+ });
36
+ }
37
+ // 2. Author hub notes — one per distinct author, with backlinks.
38
+ const byAuthor = new Map();
39
+ for (const d of decisions) {
40
+ if (!byAuthor.has(d.author))
41
+ byAuthor.set(d.author, []);
42
+ byAuthor.get(d.author).push(d);
43
+ }
44
+ for (const [author, list] of byAuthor) {
45
+ files.push({
46
+ path: `authors/${slug(author)}.md`,
47
+ content: authorHubContent(author, list),
48
+ });
49
+ }
50
+ // 3. Top-level index.
51
+ files.push({
52
+ path: "index.md",
53
+ content: decisionsIndexContent(decisions),
54
+ });
55
+ return files;
56
+ }
57
+ function indexHeader() {
58
+ return [
59
+ "---",
60
+ "type: mneme-vault",
61
+ `generated: ${new Date().toISOString()}`,
62
+ "---",
63
+ "",
64
+ "# Mneme Vault — Architecture Decisions",
65
+ "",
66
+ ].join("\n");
67
+ }
68
+ function decisionsIndexContent(decisions) {
69
+ const lines = [indexHeader()];
70
+ lines.push("This vault was auto-extracted from git history by [Mneme](https://github.com/patsa2561-art/mneme-ai).");
71
+ lines.push("");
72
+ lines.push(`**${decisions.length} decisions** across **${new Set(decisions.map((d) => d.author)).size} authors**.`);
73
+ lines.push("");
74
+ lines.push("## Recent decisions");
75
+ lines.push("");
76
+ lines.push("| Date | Decision | Source |");
77
+ lines.push("|---|---|---|");
78
+ for (const d of decisions.slice(0, 50)) {
79
+ const link = `[[decisions/${decisionFilename(d)}|${escapeMd(d.summary)}]]`;
80
+ const author = `[[authors/${slug(d.author)}|${escapeMd(d.author)}]]`;
81
+ lines.push(`| ${d.date} | ${link} | ${author} \\| \`${d.shortHash}\` |`);
82
+ }
83
+ lines.push("");
84
+ lines.push("## By author");
85
+ lines.push("");
86
+ const authors = new Map();
87
+ for (const d of decisions)
88
+ authors.set(d.author, (authors.get(d.author) ?? 0) + 1);
89
+ for (const [a, n] of [...authors.entries()].sort((x, y) => y[1] - x[1])) {
90
+ lines.push(`- [[authors/${slug(a)}|${escapeMd(a)}]] — ${n} decision${n === 1 ? "" : "s"}`);
91
+ }
92
+ lines.push("");
93
+ return lines.join("\n");
94
+ }
95
+ function decisionNoteContent(d) {
96
+ const tags = [`decisions/${d.kind}`, `author/${slug(d.author)}`].map((t) => ` - ${t}`).join("\n");
97
+ const lines = [
98
+ "---",
99
+ `date: ${d.date}`,
100
+ `author: ${escapeMd(d.author)}`,
101
+ `kind: ${d.kind}`,
102
+ `confidence: ${d.confidence}`,
103
+ `commit: ${d.shortHash}`,
104
+ "tags:",
105
+ tags,
106
+ "---",
107
+ "",
108
+ `# ${escapeMd(d.summary)}`,
109
+ "",
110
+ ];
111
+ if (d.rationale) {
112
+ lines.push(`> **Rationale:** ${escapeMd(d.rationale)}`);
113
+ lines.push("");
114
+ }
115
+ lines.push("## Source");
116
+ lines.push("");
117
+ lines.push(`- **Author:** [[authors/${slug(d.author)}|${escapeMd(d.author)}]]`);
118
+ lines.push(`- **Date:** ${d.date}`);
119
+ lines.push(`- **Commit:** \`${d.shortHash}\``);
120
+ lines.push(`- **Pattern:** \`${d.kind}\` (confidence ${d.confidence.toFixed(2)})`);
121
+ lines.push("");
122
+ lines.push("---");
123
+ lines.push("");
124
+ lines.push("← [[index|Back to index]]");
125
+ lines.push("");
126
+ return lines.join("\n");
127
+ }
128
+ function authorHubContent(author, decisions) {
129
+ const lines = [
130
+ "---",
131
+ `type: author`,
132
+ `name: ${escapeMd(author)}`,
133
+ `decisionCount: ${decisions.length}`,
134
+ "---",
135
+ "",
136
+ `# ${escapeMd(author)}`,
137
+ "",
138
+ `${decisions.length} architectural decision${decisions.length === 1 ? "" : "s"} attributed to ${escapeMd(author)} via git history.`,
139
+ "",
140
+ "## Decisions",
141
+ "",
142
+ ];
143
+ for (const d of decisions) {
144
+ lines.push(`- ${d.date} — [[decisions/${decisionFilename(d)}|${escapeMd(d.summary)}]] \`${d.shortHash}\``);
145
+ }
146
+ lines.push("");
147
+ lines.push("---");
148
+ lines.push("");
149
+ lines.push("← [[index|Back to index]]");
150
+ lines.push("");
151
+ return lines.join("\n");
152
+ }
153
+ function decisionFilename(d) {
154
+ // Obsidian-friendly: date-prefix + kind + slug of summary, kept short.
155
+ const summarySlug = slug(d.summary).slice(0, 60);
156
+ return `${d.date}-${d.kind}-${summarySlug}`;
157
+ }
158
+ // ─── Story → Obsidian ───────────────────────────────────────────────────
159
+ export function storyToVault(story, llmActSummaries) {
160
+ if (story.acts.length === 0) {
161
+ return [
162
+ {
163
+ path: `story-${slug(story.topic)}.md`,
164
+ content: `---\ntype: mneme-story\ntopic: ${escapeMd(story.topic)}\n---\n\n# The ${escapeMd(story.topic)} Story\n\n_No commits matched this topic._\n`,
165
+ },
166
+ ];
167
+ }
168
+ const lines = [
169
+ "---",
170
+ "type: mneme-story",
171
+ `topic: ${escapeMd(story.topic)}`,
172
+ `acts: ${story.acts.length}`,
173
+ `commits: ${story.totalCommits}`,
174
+ `spanDays: ${story.spanDays}`,
175
+ `generated: ${new Date().toISOString()}`,
176
+ "tags:",
177
+ " - mneme/story",
178
+ ` - topic/${slug(story.topic)}`,
179
+ "---",
180
+ "",
181
+ `# The ${escapeMd(story.topic)} Story`,
182
+ "",
183
+ `**${story.acts.length} acts · ${story.totalCommits} commits · ${story.spanDays} days**`,
184
+ "",
185
+ ];
186
+ story.acts.forEach((act, i) => {
187
+ lines.push(`## ${escapeMd(act.title)}`);
188
+ lines.push("");
189
+ lines.push(`_${act.fromDate} → ${act.toDate}_`);
190
+ lines.push("");
191
+ const summary = llmActSummaries?.get(i);
192
+ if (summary) {
193
+ lines.push(`> ${summary.replace(/\n/g, " ")}`);
194
+ lines.push("");
195
+ }
196
+ for (const c of act.commits) {
197
+ lines.push(`- ${c.authorDate.slice(0, 10)} — \`${c.shortHash || c.hash.slice(0, 7)}\` ${escapeMd(c.subject)} _(${escapeMd(c.authorName)})_`);
198
+ }
199
+ lines.push("");
200
+ });
201
+ lines.push("---");
202
+ lines.push("");
203
+ lines.push("_Generated by [Mneme](https://github.com/patsa2561-art/mneme-ai) — `mneme story " + story.topic + " --format obsidian`_");
204
+ lines.push("");
205
+ return [{ path: `story-${slug(story.topic)}.md`, content: lines.join("\n") }];
206
+ }
207
+ // ─── Experts (who-knows) → Obsidian ─────────────────────────────────────
208
+ export function expertsToVault(topic, experts) {
209
+ if (experts.length === 0) {
210
+ return [
211
+ {
212
+ path: `experts-${slug(topic)}.md`,
213
+ content: `---\ntype: mneme-experts\ntopic: ${escapeMd(topic)}\n---\n\n# Experts on ${escapeMd(topic)}\n\n_No experts found._\n`,
214
+ },
215
+ ];
216
+ }
217
+ const lines = [
218
+ "---",
219
+ "type: mneme-experts",
220
+ `topic: ${escapeMd(topic)}`,
221
+ `count: ${experts.length}`,
222
+ `generated: ${new Date().toISOString()}`,
223
+ "tags:",
224
+ ` - mneme/experts`,
225
+ ` - topic/${slug(topic)}`,
226
+ "---",
227
+ "",
228
+ `# Top experts on ${escapeMd(topic)}`,
229
+ "",
230
+ "| Tier | Name | Commits | Files | Last touch |",
231
+ "|---|---|---|---|---|",
232
+ ];
233
+ for (const e of experts) {
234
+ lines.push(`| ${e.tier} | [[authors/${slug(e.name)}|${escapeMd(e.name)}]] | ${e.commitCount} | ${e.filesTouched} | ${e.lastTouch.slice(0, 10)} |`);
235
+ }
236
+ lines.push("");
237
+ return [{ path: `experts-${slug(topic)}.md`, content: lines.join("\n") }];
238
+ }
239
+ // ─── helpers ────────────────────────────────────────────────────────────
240
+ /**
241
+ * Filesystem- and Obsidian-friendly slug. Lowercases, removes non-alnum
242
+ * except `-`, collapses whitespace and dashes.
243
+ */
244
+ export function slug(s) {
245
+ return s
246
+ .toLowerCase()
247
+ .replace(/[^\w\s-]/g, "")
248
+ .replace(/\s+/g, "-")
249
+ .replace(/-+/g, "-")
250
+ .replace(/^-|-$/g, "")
251
+ .slice(0, 80);
252
+ }
253
+ /**
254
+ * Minimal Markdown-escape — only escapes pipe (table separator) and the
255
+ * Obsidian wiki-link delimiters that would corrupt rendering.
256
+ */
257
+ export function escapeMd(s) {
258
+ return s
259
+ .replace(/\|/g, "\\|")
260
+ .replace(/\[\[/g, "[ [")
261
+ .replace(/\]\]/g, "] ]");
262
+ }
263
+ //# sourceMappingURL=obsidian.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian.js","sourceRoot":"","sources":["../../src/insights/obsidian.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=obsidian.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian.test.d.ts","sourceRoot":"","sources":["../../src/insights/obsidian.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,241 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { decisionsToVault, storyToVault, expertsToVault, slug, escapeMd, } from "./obsidian.js";
3
+ const decision = (overrides = {}) => ({
4
+ commitHash: "abcdef1234567890abcdef1234567890abcdef12",
5
+ shortHash: "abcdef1",
6
+ date: "2024-08-12",
7
+ author: "Alice",
8
+ summary: "switched from passport to JWT",
9
+ rationale: "compliance flagged session storage",
10
+ kind: "switched",
11
+ confidence: 0.9,
12
+ ...overrides,
13
+ });
14
+ describe("slug", () => {
15
+ it("lowercases and collapses whitespace", () => {
16
+ expect(slug("Hello World")).toBe("hello-world");
17
+ });
18
+ it("strips non-word characters", () => {
19
+ expect(slug("decided to use Map!")).toBe("decided-to-use-map");
20
+ });
21
+ it("collapses multiple dashes", () => {
22
+ expect(slug("foo - - bar")).toBe("foo-bar");
23
+ });
24
+ it("trims leading/trailing dashes", () => {
25
+ expect(slug("--hello--")).toBe("hello");
26
+ });
27
+ it("caps at 80 chars", () => {
28
+ expect(slug("a".repeat(200)).length).toBeLessThanOrEqual(80);
29
+ });
30
+ });
31
+ describe("escapeMd", () => {
32
+ it("escapes pipe characters (table separator)", () => {
33
+ expect(escapeMd("foo|bar")).toBe("foo\\|bar");
34
+ });
35
+ it("breaks wiki-link delimiters that would corrupt rendering", () => {
36
+ expect(escapeMd("see [[other]]")).not.toContain("[[");
37
+ expect(escapeMd("see [[other]]")).not.toContain("]]");
38
+ });
39
+ it("preserves normal text", () => {
40
+ expect(escapeMd("ordinary commit subject")).toBe("ordinary commit subject");
41
+ });
42
+ });
43
+ describe("decisionsToVault — empty case", () => {
44
+ it("returns a single index file when no decisions", () => {
45
+ const files = decisionsToVault([]);
46
+ expect(files).toHaveLength(1);
47
+ expect(files[0].path).toBe("index.md");
48
+ expect(files[0].content).toContain("No decisions extracted");
49
+ });
50
+ });
51
+ describe("decisionsToVault — full case", () => {
52
+ it("emits one note per decision plus authors plus index", () => {
53
+ const decisions = [
54
+ decision({ author: "Alice", summary: "A" }),
55
+ decision({ author: "Alice", summary: "B" }),
56
+ decision({ author: "Bob", summary: "C" }),
57
+ ];
58
+ const files = decisionsToVault(decisions);
59
+ const decisionFiles = files.filter((f) => f.path.startsWith("decisions/"));
60
+ const authorFiles = files.filter((f) => f.path.startsWith("authors/"));
61
+ const indexFiles = files.filter((f) => f.path === "index.md");
62
+ expect(decisionFiles).toHaveLength(3); // one per decision
63
+ expect(authorFiles).toHaveLength(2); // 2 distinct authors
64
+ expect(indexFiles).toHaveLength(1);
65
+ });
66
+ it("each decision note has YAML frontmatter with date, author, kind, confidence", () => {
67
+ const files = decisionsToVault([decision()]);
68
+ const note = files.find((f) => f.path.startsWith("decisions/"));
69
+ expect(note.content).toMatch(/^---\n/);
70
+ expect(note.content).toContain("date: 2024-08-12");
71
+ expect(note.content).toContain("author: Alice");
72
+ expect(note.content).toContain("kind: switched");
73
+ expect(note.content).toContain("confidence: 0.9");
74
+ expect(note.content).toContain("commit: abcdef1");
75
+ });
76
+ it("decision note links back to the author hub", () => {
77
+ const files = decisionsToVault([decision({ author: "Alice" })]);
78
+ const note = files.find((f) => f.path.startsWith("decisions/"));
79
+ expect(note.content).toContain("[[authors/alice|Alice]]");
80
+ });
81
+ it("decision note shows rationale when present", () => {
82
+ const files = decisionsToVault([decision({ rationale: "compliance reasons" })]);
83
+ const note = files.find((f) => f.path.startsWith("decisions/"));
84
+ expect(note.content).toContain("Rationale");
85
+ expect(note.content).toContain("compliance reasons");
86
+ });
87
+ it("author hub lists all decisions for that author", () => {
88
+ const decisions = [
89
+ decision({ author: "Alice", summary: "A1", date: "2024-01-01" }),
90
+ decision({ author: "Alice", summary: "A2", date: "2024-02-01" }),
91
+ ];
92
+ const files = decisionsToVault(decisions);
93
+ const hub = files.find((f) => f.path === "authors/alice.md");
94
+ expect(hub.content).toContain("A1");
95
+ expect(hub.content).toContain("A2");
96
+ expect(hub.content).toContain("decisionCount: 2");
97
+ });
98
+ it("index.md contains a wiki-linked table of recent decisions", () => {
99
+ const files = decisionsToVault([decision({ summary: "use Postgres" })]);
100
+ const index = files.find((f) => f.path === "index.md");
101
+ expect(index.content).toContain("[[decisions/");
102
+ expect(index.content).toContain("use Postgres");
103
+ });
104
+ it("filenames are filesystem- and Obsidian-safe", () => {
105
+ const tricky = decision({ summary: "decided to use C++ / Rust!" });
106
+ const files = decisionsToVault([tricky]);
107
+ for (const f of files) {
108
+ expect(f.path).not.toMatch(/[/\\:*?"<>|]/g.source.replace(/\//g, ""));
109
+ // No spaces in filenames (Obsidian works fine with them, but slugs are cleaner).
110
+ expect(f.path.split("/").pop()).not.toContain(" ");
111
+ }
112
+ });
113
+ });
114
+ describe("storyToVault", () => {
115
+ const sampleStory = {
116
+ topic: "authentication",
117
+ totalCommits: 3,
118
+ spanDays: 90,
119
+ acts: [
120
+ {
121
+ id: "initial",
122
+ title: "Act I — The Beginning",
123
+ fromDate: "2024-01-01",
124
+ toDate: "2024-01-01",
125
+ commits: [
126
+ {
127
+ hash: "a1",
128
+ shortHash: "a1",
129
+ authorName: "alice",
130
+ authorEmail: "a@x",
131
+ authorDate: "2024-01-01T00:00:00Z",
132
+ committerDate: "2024-01-01T00:00:00Z",
133
+ subject: "feat: passport",
134
+ body: "",
135
+ parents: [],
136
+ files: [],
137
+ },
138
+ ],
139
+ },
140
+ {
141
+ id: "refactor",
142
+ title: "Act II — The Refactor",
143
+ fromDate: "2024-02-01",
144
+ toDate: "2024-04-01",
145
+ commits: [
146
+ {
147
+ hash: "b1",
148
+ shortHash: "b1",
149
+ authorName: "alice",
150
+ authorEmail: "a@x",
151
+ authorDate: "2024-02-01T00:00:00Z",
152
+ committerDate: "2024-02-01T00:00:00Z",
153
+ subject: "refactor: replace passport",
154
+ body: "",
155
+ parents: [],
156
+ files: [],
157
+ },
158
+ ],
159
+ },
160
+ ],
161
+ };
162
+ it("returns a single .md file for a story", () => {
163
+ const files = storyToVault(sampleStory);
164
+ expect(files).toHaveLength(1);
165
+ expect(files[0].path).toBe("story-authentication.md");
166
+ });
167
+ it("includes frontmatter with topic, acts, commits, spanDays", () => {
168
+ const files = storyToVault(sampleStory);
169
+ const c = files[0].content;
170
+ expect(c).toContain("type: mneme-story");
171
+ expect(c).toContain("topic: authentication");
172
+ expect(c).toContain("acts: 2");
173
+ expect(c).toContain("commits: 3");
174
+ expect(c).toContain("spanDays: 90");
175
+ });
176
+ it("renders each act as a heading + commit list", () => {
177
+ const c = storyToVault(sampleStory)[0].content;
178
+ expect(c).toContain("## Act I");
179
+ expect(c).toContain("## Act II");
180
+ expect(c).toContain("feat: passport");
181
+ expect(c).toContain("refactor: replace passport");
182
+ });
183
+ it("includes LLM act summaries when provided", () => {
184
+ const summaries = new Map([
185
+ [0, "Started with passport.js OAuth"],
186
+ [1, "Replaced passport with custom JWT"],
187
+ ]);
188
+ const c = storyToVault(sampleStory, summaries)[0].content;
189
+ expect(c).toContain("Started with passport.js");
190
+ expect(c).toContain("Replaced passport");
191
+ });
192
+ it("falls back to a clean note when story has no acts", () => {
193
+ const empty = { topic: "x", totalCommits: 0, spanDays: 0, acts: [] };
194
+ const files = storyToVault(empty);
195
+ expect(files[0].content).toContain("No commits matched");
196
+ });
197
+ });
198
+ describe("expertsToVault", () => {
199
+ const sampleExperts = [
200
+ {
201
+ name: "Alice",
202
+ email: "a@x",
203
+ commitCount: 20,
204
+ filesTouched: 50,
205
+ lastTouch: "2026-04-01T00:00:00Z",
206
+ score: 4.0,
207
+ tier: "definitive",
208
+ },
209
+ {
210
+ name: "Bob",
211
+ email: "b@x",
212
+ commitCount: 5,
213
+ filesTouched: 12,
214
+ lastTouch: "2025-12-01T00:00:00Z",
215
+ score: 1.5,
216
+ tier: "stale",
217
+ },
218
+ ];
219
+ it("returns a single experts note", () => {
220
+ const files = expertsToVault("stripe", sampleExperts);
221
+ expect(files).toHaveLength(1);
222
+ expect(files[0].path).toBe("experts-stripe.md");
223
+ });
224
+ it("renders a markdown table with tier, name, counts, last touch", () => {
225
+ const c = expertsToVault("stripe", sampleExperts)[0].content;
226
+ expect(c).toContain("| Tier | Name | Commits | Files | Last touch |");
227
+ expect(c).toContain("definitive");
228
+ expect(c).toContain("Alice");
229
+ expect(c).toContain("20");
230
+ });
231
+ it("links each name to the author hub", () => {
232
+ const c = expertsToVault("stripe", sampleExperts)[0].content;
233
+ expect(c).toContain("[[authors/alice|Alice]]");
234
+ expect(c).toContain("[[authors/bob|Bob]]");
235
+ });
236
+ it("falls back to a friendly note when no experts found", () => {
237
+ const c = expertsToVault("nothing", [])[0].content;
238
+ expect(c).toContain("No experts found");
239
+ });
240
+ });
241
+ //# sourceMappingURL=obsidian.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian.test.js","sourceRoot":"","sources":["../../src/insights/obsidian.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,IAAI,EACJ,QAAQ,GACT,MAAM,eAAe,CAAC;AAKvB,MAAM,QAAQ,GAAG,CAAC,YAAwC,EAAE,EAAqB,EAAE,CAAC,CAAC;IACnF,UAAU,EAAE,0CAA0C;IACtD,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,YAAY;IAClB,MAAM,EAAE,OAAO;IACf,OAAO,EAAE,+BAA+B;IACxC,SAAS,EAAE,oCAAoC;IAC/C,IAAI,EAAE,UAAU;IAChB,UAAU,EAAE,GAAG;IACf,GAAG,SAAS;CACb,CAAC,CAAC;AAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IACpB,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,KAAK,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,SAAS,GAAG;YAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;YAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;YAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;SAC1C,CAAC;QACF,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAE1C,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;QACvE,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;QAE9D,MAAM,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB;QAC1D,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAG,qBAAqB;QAC5D,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAE,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAE,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,CAAC;QAChF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAE,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,SAAS,GAAG;YAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;YAChE,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;SACjE,CAAC;QACF,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAE,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAE,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;YACtE,iFAAiF;YACjF,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,MAAM,WAAW,GAAU;QACzB,KAAK,EAAE,gBAAgB;QACvB,YAAY,EAAE,CAAC;QACf,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE;YACJ;gBACE,EAAE,EAAE,SAAS;gBACb,KAAK,EAAE,uBAAuB;gBAC9B,QAAQ,EAAE,YAAY;gBACtB,MAAM,EAAE,YAAY;gBACpB,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,IAAI;wBACV,SAAS,EAAE,IAAI;wBACf,UAAU,EAAE,OAAO;wBACnB,WAAW,EAAE,KAAK;wBAClB,UAAU,EAAE,sBAAsB;wBAClC,aAAa,EAAE,sBAAsB;wBACrC,OAAO,EAAE,gBAAgB;wBACzB,IAAI,EAAE,EAAE;wBACR,OAAO,EAAE,EAAE;wBACX,KAAK,EAAE,EAAE;qBACV;iBACF;aACF;YACD;gBACE,EAAE,EAAE,UAAU;gBACd,KAAK,EAAE,uBAAuB;gBAC9B,QAAQ,EAAE,YAAY;gBACtB,MAAM,EAAE,YAAY;gBACpB,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,IAAI;wBACV,SAAS,EAAE,IAAI;wBACf,UAAU,EAAE,OAAO;wBACnB,WAAW,EAAE,KAAK;wBAClB,UAAU,EAAE,sBAAsB;wBAClC,aAAa,EAAE,sBAAsB;wBACrC,OAAO,EAAE,4BAA4B;wBACrC,IAAI,EAAE,EAAE;wBACR,OAAO,EAAE,EAAE;wBACX,KAAK,EAAE,EAAE;qBACV;iBACF;aACF;SACF;KACF,CAAC;IAEF,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAC7C,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC/B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAClC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAChC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAiB;YACxC,CAAC,CAAC,EAAE,gCAAgC,CAAC;YACrC,CAAC,CAAC,EAAE,mCAAmC,CAAC;SACzC,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,YAAY,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC;QAC3D,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,KAAK,GAAU,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAC5E,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,MAAM,aAAa,GAAsB;QACvC;YACE,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,KAAK;YACZ,WAAW,EAAE,EAAE;YACf,YAAY,EAAE,EAAE;YAChB,SAAS,EAAE,sBAAsB;YACjC,KAAK,EAAE,GAAG;YACV,IAAI,EAAE,YAAY;SACnB;QACD;YACE,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,KAAK;YACZ,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,EAAE;YAChB,SAAS,EAAE,sBAAsB;YACjC,KAAK,EAAE,GAAG;YACV,IAAI,EAAE,OAAO;SACd;KACF,CAAC;IAEF,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,CAAC,GAAG,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC;QAC9D,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,gDAAgD,CAAC,CAAC;QACtE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAClC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,GAAG,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC;QAC9D,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAC/C,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,GAAG,cAAc,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC;QACpD,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `stack-trace` — given an error / stack trace, find the commits that
3
+ * touched each frame and any past incidents at the same locations.
4
+ *
5
+ * Why this exists: when a prod bug fires, the most useful question is
6
+ * "have we seen this here before?" Mneme already indexes git + incidents.
7
+ * Parsing a stack trace and querying both is a small composition of pieces
8
+ * we already have.
9
+ *
10
+ * Supported formats (frame parser):
11
+ * - JS/TS: " at functionName (path/to/file.ts:42:15)"
12
+ * - Python: " File "path/to/file.py", line 42, in functionName"
13
+ * - Go: "goroutine 1 [running]: ... path/to/file.go:42 +0x..."
14
+ * - Java: " at com.example.Foo.bar(Foo.java:42)"
15
+ *
16
+ * Pure parsing — no I/O. Composing with retrieval lives in the CLI command.
17
+ */
18
+ export interface StackFrame {
19
+ /** File path as it appears in the trace. May be absolute, relative, or module-qualified. */
20
+ file: string;
21
+ /** Line number where the frame is. 0 when absent. */
22
+ line: number;
23
+ /** Function/method name when extractable; may be empty. */
24
+ function?: string;
25
+ /** Programming language detected from the frame format. */
26
+ language: "js" | "python" | "go" | "java" | "unknown";
27
+ }
28
+ /**
29
+ * Parse a multi-line stack trace into ordered frames. Frames are deduped
30
+ * when the same (file, line) appears consecutively. Languages that share
31
+ * patterns (e.g. .ts files might also match the python file regex if
32
+ * weirdly formatted) are disambiguated by extension first.
33
+ */
34
+ export declare function parseStackTrace(text: string): StackFrame[];
35
+ /**
36
+ * Heuristic: detect whether the trace appears to be from a particular
37
+ * language. Useful when displaying frame analysis ("Python traceback").
38
+ */
39
+ export declare function detectLanguage(text: string): StackFrame["language"];
40
+ //# sourceMappingURL=stack-trace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack-trace.d.ts","sourceRoot":"","sources":["../../src/insights/stack-trace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,UAAU;IACzB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,EAAE,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;CACvD;AA2DD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAyB1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CASnE"}