@betterdb/memory 0.1.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.
@@ -0,0 +1,321 @@
1
+ import { config } from "../config.js";
2
+ import type { ModelClient } from "../client/model.js";
3
+ import type { ValkeyClient } from "../client/valkey.js";
4
+ import { SessionSummarySchema, type EpisodicMemory } from "./schema.js";
5
+ import { computeInitialImportance, SessionCapture } from "./capture.js";
6
+
7
+ // --- Cosine Similarity ---
8
+
9
+ export function cosineSimilarity(a: number[], b: number[]): number {
10
+ let dot = 0;
11
+ let magA = 0;
12
+ let magB = 0;
13
+ for (let i = 0; i < a.length; i++) {
14
+ const av = a[i] ?? 0;
15
+ const bv = b[i] ?? 0;
16
+ dot += av * bv;
17
+ magA += av * av;
18
+ magB += bv * bv;
19
+ }
20
+ const denom = Math.sqrt(magA) * Math.sqrt(magB);
21
+ return denom === 0 ? 0 : dot / denom;
22
+ }
23
+
24
+ // --- Aging Pipeline ---
25
+
26
+ export class AgingPipeline {
27
+ private valkeyClient: ValkeyClient;
28
+ private modelClient: ModelClient;
29
+
30
+ constructor(valkeyClient: ValkeyClient, modelClient: ModelClient) {
31
+ this.valkeyClient = valkeyClient;
32
+ this.modelClient = modelClient;
33
+ }
34
+
35
+ // --- Decay ---
36
+
37
+ async runDecay(
38
+ project?: string,
39
+ ): Promise<{ processed: number; flagged: number }> {
40
+ const memoryIds = await this.valkeyClient.listMemoryIds(project);
41
+ let processed = 0;
42
+ let flagged = 0;
43
+
44
+ for (const id of memoryIds) {
45
+ const memory = await this.valkeyClient.getMemory(id);
46
+ if (!memory) continue;
47
+
48
+ const daysSince =
49
+ (Date.now() - new Date(memory.lastAccessed).getTime()) /
50
+ (1000 * 60 * 60 * 24);
51
+ const newScore =
52
+ memory.importanceScore *
53
+ Math.pow(config.memory.decayRate, daysSince);
54
+
55
+ await this.valkeyClient.updateImportance(id, newScore);
56
+ processed++;
57
+
58
+ if (newScore < config.memory.compressThreshold) {
59
+ await this.valkeyClient.pushCompressQueue(id);
60
+ flagged++;
61
+ }
62
+ }
63
+
64
+ return { processed, flagged };
65
+ }
66
+
67
+ // --- Compression ---
68
+
69
+ async runCompression(): Promise<{ merged: number; deleted: number }> {
70
+ const ids = await this.valkeyClient.popCompressQueue(50);
71
+ if (ids.length === 0) return { merged: 0, deleted: 0 };
72
+
73
+ // Fetch memories with embeddings
74
+ const entries: Array<{
75
+ memory: EpisodicMemory;
76
+ embedding: number[];
77
+ }> = [];
78
+
79
+ for (const id of ids) {
80
+ const memory = await this.valkeyClient.getMemory(id);
81
+ const embedding = await this.valkeyClient.getMemoryEmbedding(id);
82
+ if (memory && embedding) {
83
+ entries.push({ memory, embedding });
84
+ }
85
+ }
86
+
87
+ // Group by project
88
+ const byProject = new Map<
89
+ string,
90
+ Array<{ memory: EpisodicMemory; embedding: number[] }>
91
+ >();
92
+ for (const entry of entries) {
93
+ const group = byProject.get(entry.memory.project) ?? [];
94
+ group.push(entry);
95
+ byProject.set(entry.memory.project, group);
96
+ }
97
+
98
+ let merged = 0;
99
+ let deleted = 0;
100
+
101
+ for (const [, group] of byProject) {
102
+ // Batch size guard: only process 100 lowest-importance per project
103
+ const sorted = group
104
+ .sort((a, b) => a.memory.importanceScore - b.memory.importanceScore)
105
+ .slice(0, 100);
106
+
107
+ if (sorted.length < group.length) {
108
+ console.error(
109
+ `[betterdb] Project group exceeds 100 memories, processing only lowest-importance 100. Additional runs needed.`,
110
+ );
111
+ }
112
+
113
+ // Find clusters of similar memories
114
+ const used = new Set<number>();
115
+ const clusters: Array<
116
+ Array<{ memory: EpisodicMemory; embedding: number[] }>
117
+ > = [];
118
+
119
+ for (let i = 0; i < sorted.length; i++) {
120
+ if (used.has(i)) continue;
121
+ const cluster = [sorted[i]!];
122
+ used.add(i);
123
+
124
+ for (let j = i + 1; j < sorted.length; j++) {
125
+ if (used.has(j)) continue;
126
+ // Check if similar to all cluster members
127
+ const similar = cluster.every(
128
+ (c) =>
129
+ cosineSimilarity(c.embedding, sorted[j]!.embedding) > 0.85,
130
+ );
131
+ if (similar) {
132
+ cluster.push(sorted[j]!);
133
+ used.add(j);
134
+ }
135
+ }
136
+
137
+ clusters.push(cluster);
138
+ }
139
+
140
+ // Process clusters
141
+ for (const cluster of clusters) {
142
+ if (cluster.length >= 3) {
143
+ // Merge cluster into a single memory
144
+ const combinedTranscript = cluster
145
+ .map(
146
+ (c) =>
147
+ `Session ${c.memory.memoryId}: ${c.memory.summary.oneLineSummary}\n` +
148
+ `Decisions: ${c.memory.summary.decisions.join("; ")}\n` +
149
+ `Patterns: ${c.memory.summary.patterns.join("; ")}`,
150
+ )
151
+ .join("\n\n");
152
+
153
+ const mergedSummary =
154
+ await this.modelClient.summarize(combinedTranscript);
155
+ const mergedEmbedding = await this.modelClient.embed(
156
+ mergedSummary.oneLineSummary,
157
+ );
158
+
159
+ const avgImportance =
160
+ cluster.reduce((sum, c) => sum + c.memory.importanceScore, 0) /
161
+ cluster.length;
162
+
163
+ const newMemory: EpisodicMemory = {
164
+ memoryId: crypto.randomUUID(),
165
+ project: cluster[0]!.memory.project,
166
+ branch: cluster[0]!.memory.branch,
167
+ timestamp: new Date().toISOString(),
168
+ summary: mergedSummary,
169
+ importanceScore: avgImportance,
170
+ accessCount: 0,
171
+ lastAccessed: new Date().toISOString(),
172
+ };
173
+
174
+ await this.valkeyClient.storeMemory(newMemory, mergedEmbedding);
175
+
176
+ // Delete originals
177
+ for (const c of cluster) {
178
+ await this.valkeyClient.deleteMemory(c.memory.memoryId);
179
+ }
180
+
181
+ merged += cluster.length;
182
+ } else if (cluster.length === 1) {
183
+ const m = cluster[0]!.memory;
184
+ const daysSince =
185
+ (Date.now() - new Date(m.lastAccessed).getTime()) /
186
+ (1000 * 60 * 60 * 24);
187
+
188
+ if (m.importanceScore < 0.05 && daysSince > 90) {
189
+ await this.valkeyClient.deleteMemory(m.memoryId);
190
+ deleted++;
191
+ }
192
+ }
193
+ }
194
+ }
195
+
196
+ return { merged, deleted };
197
+ }
198
+
199
+ // --- Distillation ---
200
+
201
+ async runDistillation(
202
+ project: string,
203
+ ): Promise<{ distilled: number }> {
204
+ const memoryIds = await this.valkeyClient.listMemoryIds(project, 0.5);
205
+ const memories: EpisodicMemory[] = [];
206
+
207
+ for (const id of memoryIds) {
208
+ const memory = await this.valkeyClient.getMemory(id);
209
+ if (memory) memories.push(memory);
210
+ }
211
+
212
+ if (memories.length < config.memory.distillMinSessions) {
213
+ return { distilled: 0 };
214
+ }
215
+
216
+ // Count pattern occurrences
217
+ const patternCounts = new Map<string, string[]>();
218
+ for (const m of memories) {
219
+ for (const pattern of m.summary.patterns) {
220
+ const normalized = pattern.toLowerCase().trim();
221
+ const sources = patternCounts.get(normalized) ?? [];
222
+ sources.push(m.memoryId);
223
+ patternCounts.set(normalized, sources);
224
+ }
225
+ }
226
+
227
+ let distilled = 0;
228
+
229
+ for (const [pattern, sourceIds] of patternCounts) {
230
+ if (sourceIds.length < config.memory.distillMinSessions) continue;
231
+
232
+ const distillPrompt = `Distill this recurring pattern into a single factual sentence:\nPattern: "${pattern}"\nAppeared in ${sourceIds.length} sessions.`;
233
+ const summary = await this.modelClient.summarize(distillPrompt);
234
+
235
+ const entry = {
236
+ entryId: crypto.randomUUID(),
237
+ project,
238
+ topic: pattern.slice(0, 100),
239
+ fact: summary.oneLineSummary,
240
+ confidence: Math.min(0.5 + sourceIds.length * 0.1, 1.0),
241
+ sourceMemoryIds: sourceIds.slice(0, 10),
242
+ lastUpdated: new Date().toISOString(),
243
+ accessCount: 0,
244
+ };
245
+
246
+ await this.valkeyClient.storeKnowledge(entry);
247
+ distilled++;
248
+ }
249
+
250
+ return { distilled };
251
+ }
252
+
253
+ // --- Ingest Queue Processing ---
254
+
255
+ async processIngestQueue(): Promise<{ processed: number }> {
256
+ const items = await this.valkeyClient.popIngestQueue(20);
257
+ let processed = 0;
258
+
259
+ for (const item of items) {
260
+ try {
261
+ const summary = await this.modelClient.summarize(item.transcript);
262
+ const embedding = await this.modelClient.embed(
263
+ summary.oneLineSummary,
264
+ );
265
+ const importance = computeInitialImportance(summary);
266
+
267
+ const meta = item.meta as Record<string, string>;
268
+ const memory: EpisodicMemory = {
269
+ memoryId: crypto.randomUUID(),
270
+ project: meta["project"] ?? "unknown",
271
+ branch: meta["branch"] ?? "unknown",
272
+ timestamp: meta["timestamp"] ?? new Date().toISOString(),
273
+ summary,
274
+ importanceScore: importance,
275
+ accessCount: 0,
276
+ lastAccessed: new Date().toISOString(),
277
+ };
278
+
279
+ await this.valkeyClient.storeMemory(memory, embedding);
280
+ processed++;
281
+ } catch (err) {
282
+ console.error("[betterdb] Failed to process queued transcript:", err);
283
+ // Re-queue on failure
284
+ await this.valkeyClient.pushIngestQueue(
285
+ item.transcript,
286
+ item.meta,
287
+ );
288
+ break;
289
+ }
290
+ }
291
+
292
+ return { processed };
293
+ }
294
+
295
+ // --- Full Pipeline ---
296
+
297
+ async runFullPipeline(project?: string): Promise<void> {
298
+ console.error("[betterdb] Starting aging pipeline...");
299
+
300
+ const { processed: ingested } = await this.processIngestQueue();
301
+ console.error(`[betterdb] Ingest queue: processed ${ingested} items`);
302
+
303
+ const { processed, flagged } = await this.runDecay(project);
304
+ console.error(
305
+ `[betterdb] Decay: processed ${processed}, flagged ${flagged} for compression`,
306
+ );
307
+
308
+ const { merged, deleted } = await this.runCompression();
309
+ console.error(
310
+ `[betterdb] Compression: merged ${merged}, deleted ${deleted}`,
311
+ );
312
+
313
+ if (project) {
314
+ const { distilled } = await this.runDistillation(project);
315
+ console.error(`[betterdb] Distillation: distilled ${distilled} entries`);
316
+ }
317
+
318
+ await this.valkeyClient.setLastAgingRun(new Date());
319
+ console.error("[betterdb] Aging pipeline complete.");
320
+ }
321
+ }
@@ -0,0 +1,122 @@
1
+ import type { ModelClient } from "../client/model.js";
2
+ import type { SessionEvent, SessionSummary } from "./schema.js";
3
+
4
+ // --- Git Helpers ---
5
+
6
+ export function getGitBranch(): string {
7
+ try {
8
+ const result = Bun.spawnSync(["git", "rev-parse", "--abbrev-ref", "HEAD"]);
9
+ const output = result.stdout.toString().trim();
10
+ return output || "unknown";
11
+ } catch {
12
+ return "unknown";
13
+ }
14
+ }
15
+
16
+ export function getGitLog(n = 5): string {
17
+ try {
18
+ const result = Bun.spawnSync([
19
+ "git",
20
+ "log",
21
+ `--oneline`,
22
+ `-n`,
23
+ String(n),
24
+ "--format=%s",
25
+ ]);
26
+ return result.stdout.toString().trim();
27
+ } catch {
28
+ return "";
29
+ }
30
+ }
31
+
32
+ export function getStagedFiles(): string[] {
33
+ try {
34
+ const result = Bun.spawnSync([
35
+ "git",
36
+ "diff",
37
+ "--cached",
38
+ "--name-only",
39
+ ]);
40
+ const output = result.stdout.toString().trim();
41
+ return output ? output.split("\n") : [];
42
+ } catch {
43
+ return [];
44
+ }
45
+ }
46
+
47
+ export function getCwdProject(): string {
48
+ try {
49
+ const parts = process.cwd().split("/");
50
+ return parts[parts.length - 1] ?? "unknown";
51
+ } catch {
52
+ return "unknown";
53
+ }
54
+ }
55
+
56
+ // --- Importance Scoring ---
57
+
58
+ export function computeInitialImportance(summary: SessionSummary): number {
59
+ let score = 0.3;
60
+ score += Math.min(summary.problemsSolved.length * 0.15, 0.3);
61
+ score += Math.min(summary.decisions.length * 0.05, 0.15);
62
+ score += Math.min(summary.filesChanged.length * 0.02, 0.1);
63
+ score += summary.openThreads.length > 0 ? 0.1 : 0;
64
+ score += summary.patterns.length > 0 ? 0.05 : 0;
65
+ return Math.min(score, 1.0);
66
+ }
67
+
68
+ // --- Session Capture ---
69
+
70
+ export class SessionCapture {
71
+ private events: SessionEvent[] = [];
72
+
73
+ addEvent(event: SessionEvent): void {
74
+ this.events.push(event);
75
+ }
76
+
77
+ buildTranscript(): string {
78
+ return this.events
79
+ .map((e) => {
80
+ const filePart = e.filePath ? ` [${e.filePath}]` : "";
81
+ return `[${e.timestamp}] ${e.eventType}${filePart}: ${e.content}`;
82
+ })
83
+ .join("\n");
84
+ }
85
+
86
+ async capture(client: ModelClient): Promise<SessionSummary> {
87
+ const transcript = this.buildTranscript();
88
+ if (!transcript.trim()) {
89
+ const { SessionSummarySchema } = await import("./schema.js");
90
+ return SessionSummarySchema.parse({});
91
+ }
92
+ return client.summarize(transcript);
93
+ }
94
+
95
+ async getQueryContext(): Promise<string> {
96
+ const parts: string[] = [];
97
+
98
+ const project = getCwdProject();
99
+ parts.push(`Project: ${project}`);
100
+
101
+ const branch = getGitBranch();
102
+ if (branch !== "unknown") {
103
+ parts.push(`Branch: ${branch}`);
104
+ }
105
+
106
+ const gitLog = getGitLog(5);
107
+ if (gitLog) {
108
+ parts.push(`Recent commits:\n${gitLog}`);
109
+ }
110
+
111
+ const staged = getStagedFiles();
112
+ if (staged.length > 0) {
113
+ parts.push(`Staged files: ${staged.join(", ")}`);
114
+ }
115
+
116
+ return parts.join("\n");
117
+ }
118
+
119
+ get eventCount(): number {
120
+ return this.events.length;
121
+ }
122
+ }
@@ -0,0 +1,114 @@
1
+ import { config } from "../config.js";
2
+ import type { ModelClient } from "../client/model.js";
3
+ import type { ValkeyClient } from "../client/valkey.js";
4
+ import type { EpisodicMemory } from "./schema.js";
5
+ import { AgingPipeline } from "./aging.js";
6
+
7
+ // --- Memory Retriever ---
8
+
9
+ export class MemoryRetriever {
10
+ private valkeyClient: ValkeyClient;
11
+ private modelClient: ModelClient;
12
+ private agingPipeline: AgingPipeline;
13
+
14
+ constructor(valkeyClient: ValkeyClient, modelClient: ModelClient) {
15
+ this.valkeyClient = valkeyClient;
16
+ this.modelClient = modelClient;
17
+ this.agingPipeline = new AgingPipeline(valkeyClient, modelClient);
18
+ }
19
+
20
+ async retrieve(
21
+ queryContext: string,
22
+ project: string,
23
+ ): Promise<EpisodicMemory[]> {
24
+ await this.maybeRunAging(project);
25
+
26
+ const embedding = await this.modelClient.embed(queryContext);
27
+
28
+ const topK = config.memory.maxContextMemories * 2;
29
+ const candidates = await this.valkeyClient.searchMemories(
30
+ embedding,
31
+ project,
32
+ topK,
33
+ );
34
+
35
+ const now = Date.now();
36
+ const scored = candidates
37
+ .filter((m) => m.importanceScore >= 0.1)
38
+ .map((m) => {
39
+ const daysSince =
40
+ (now - new Date(m.lastAccessed).getTime()) / (1000 * 60 * 60 * 24);
41
+ const recencyFactor = Math.pow(
42
+ config.memory.decayRate,
43
+ Math.max(daysSince, 0),
44
+ );
45
+ return {
46
+ memory: m,
47
+ score: m.importanceScore * recencyFactor,
48
+ };
49
+ })
50
+ .sort((a, b) => b.score - a.score)
51
+ .slice(0, config.memory.maxContextMemories);
52
+
53
+ // Fire-and-forget access increments
54
+ for (const { memory } of scored) {
55
+ this.valkeyClient.incrementAccess(memory.memoryId).catch(() => {});
56
+ }
57
+
58
+ return scored.map((s) => s.memory);
59
+ }
60
+
61
+ async maybeRunAging(project: string): Promise<void> {
62
+ const lastRun = await this.valkeyClient.getLastAgingRun();
63
+ const hoursAgo = lastRun
64
+ ? (Date.now() - lastRun.getTime()) / (1000 * 60 * 60)
65
+ : Infinity;
66
+
67
+ if (hoursAgo >= config.memory.agingIntervalHours) {
68
+ await this.agingPipeline.runDecay(project);
69
+ await this.valkeyClient.setLastAgingRun(new Date());
70
+ }
71
+ }
72
+ }
73
+
74
+ // --- Format for Injection ---
75
+
76
+ export function formatForInjection(memories: EpisodicMemory[]): string {
77
+ if (memories.length === 0) return "";
78
+
79
+ const sections: string[] = [
80
+ `# BetterDB Session Context`,
81
+ `_Retrieved ${memories.length} memories. Auto-generated — do not edit._`,
82
+ ];
83
+
84
+ // Per-memory summaries — this is the most important section
85
+ sections.push(`\n## Session Memories`);
86
+ for (const m of memories) {
87
+ const date = m.timestamp.split("T")[0];
88
+ sections.push(`- **[${date}]** ${m.summary.oneLineSummary}`);
89
+ for (const d of m.summary.decisions) {
90
+ sections.push(` - Decision: ${d}`);
91
+ }
92
+ for (const p of m.summary.problemsSolved) {
93
+ sections.push(` - Solved: ${p.problem} → ${p.resolution}`);
94
+ }
95
+ for (const t of m.summary.openThreads) {
96
+ sections.push(` - Open: ${t}`);
97
+ }
98
+ }
99
+
100
+ // Aggregated files across all memories
101
+ const files = new Set<string>();
102
+ for (const m of memories) {
103
+ for (const f of m.summary.filesChanged) files.add(f);
104
+ }
105
+
106
+ if (files.size > 0) {
107
+ sections.push(
108
+ `\n## Files with History`,
109
+ ...[...files].slice(0, 10).map((f) => `- ${f}`),
110
+ );
111
+ }
112
+
113
+ return sections.join("\n");
114
+ }
@@ -0,0 +1,111 @@
1
+ import { z } from "zod";
2
+
3
+ // --- Session Events ---
4
+
5
+ export const SessionEventSchema = z.object({
6
+ sessionId: z.string(),
7
+ timestamp: z.string().datetime(),
8
+ eventType: z.enum(["tool_call", "tool_result", "error", "file_change"]),
9
+ content: z.string(),
10
+ filePath: z.string().optional(),
11
+ });
12
+
13
+ export type SessionEvent = z.infer<typeof SessionEventSchema>;
14
+
15
+ // --- Session Summary (output of summarizer) ---
16
+
17
+ export const SessionSummarySchema = z.object({
18
+ decisions: z.array(z.string()).max(10).default([]),
19
+ patterns: z.array(z.string()).max(5).default([]),
20
+ problemsSolved: z
21
+ .array(
22
+ z.object({
23
+ problem: z.string(),
24
+ resolution: z.string(),
25
+ }),
26
+ )
27
+ .max(5)
28
+ .default([]),
29
+ openThreads: z.array(z.string()).max(5).default([]),
30
+ filesChanged: z.array(z.string()).default([]),
31
+ oneLineSummary: z
32
+ .string()
33
+ .default("Session recorded — summary unavailable"),
34
+ });
35
+
36
+ export type SessionSummary = z.infer<typeof SessionSummarySchema>;
37
+
38
+ // --- Episodic Memory ---
39
+
40
+ export const EpisodicMemorySchema = z.object({
41
+ memoryId: z.string().uuid(),
42
+ project: z.string(),
43
+ branch: z.string(),
44
+ timestamp: z.string().datetime(),
45
+ summary: SessionSummarySchema,
46
+ importanceScore: z.number().min(0).max(1),
47
+ accessCount: z.number().int().nonnegative(),
48
+ lastAccessed: z.string().datetime(),
49
+ });
50
+
51
+ export type EpisodicMemory = z.infer<typeof EpisodicMemorySchema>;
52
+
53
+ // --- Knowledge Entry ---
54
+
55
+ export const KnowledgeEntrySchema = z.object({
56
+ entryId: z.string().uuid(),
57
+ project: z.string(),
58
+ topic: z.string(),
59
+ fact: z.string(),
60
+ confidence: z.number().min(0).max(1),
61
+ sourceMemoryIds: z.array(z.string().uuid()),
62
+ lastUpdated: z.string().datetime(),
63
+ accessCount: z.number().int().nonnegative(),
64
+ });
65
+
66
+ export type KnowledgeEntry = z.infer<typeof KnowledgeEntrySchema>;
67
+
68
+ // --- Hook Payloads (Claude Code Hooks API) ---
69
+ // See: https://docs.anthropic.com/en/docs/claude-code/hooks
70
+
71
+ const BaseHookPayload = z.object({
72
+ session_id: z.string(),
73
+ transcript_path: z.string().optional(),
74
+ cwd: z.string().optional(),
75
+ hook_event_name: z.string(),
76
+ });
77
+
78
+ export const SessionStartPayload = BaseHookPayload.extend({
79
+ hook_event_name: z.literal("SessionStart"),
80
+ source: z.enum(["startup", "resume", "clear", "compact"]).optional(),
81
+ });
82
+
83
+ export const StopPayload = BaseHookPayload.extend({
84
+ hook_event_name: z.literal("Stop"),
85
+ });
86
+
87
+ export const PreToolUsePayload = BaseHookPayload.extend({
88
+ hook_event_name: z.literal("PreToolUse"),
89
+ tool_name: z.string(),
90
+ tool_input: z.record(z.unknown()).optional(),
91
+ });
92
+
93
+ export const PostToolUsePayload = BaseHookPayload.extend({
94
+ hook_event_name: z.literal("PostToolUse"),
95
+ tool_name: z.string(),
96
+ tool_input: z.record(z.unknown()).optional(),
97
+ tool_result: z.unknown().optional(),
98
+ });
99
+
100
+ export const HookPayloadSchema = z.discriminatedUnion("hook_event_name", [
101
+ SessionStartPayload,
102
+ StopPayload,
103
+ PreToolUsePayload,
104
+ PostToolUsePayload,
105
+ ]);
106
+
107
+ export type HookPayload = z.infer<typeof HookPayloadSchema>;
108
+ export type SessionStartHookPayload = z.infer<typeof SessionStartPayload>;
109
+ export type StopHookPayload = z.infer<typeof StopPayload>;
110
+ export type PreToolUseHookPayload = z.infer<typeof PreToolUsePayload>;
111
+ export type PostToolUseHookPayload = z.infer<typeof PostToolUsePayload>;
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noUncheckedIndexedAccess": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "outDir": "./dist",
16
+ "rootDir": ".",
17
+ "types": ["bun-types"]
18
+ },
19
+ "include": ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }