From 696cf70c240e67bc75dcff9abcb12a359327f60c Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 21:58:49 +0000 Subject: [PATCH 1/9] feat: add traul_meta table for version tracking Co-Authored-By: Claude Sonnet 4.6 --- src/db/schema.ts | 5 +++++ test/db/schema.test.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/db/schema.ts b/src/db/schema.ts index d070887..ea8729b 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -117,6 +117,11 @@ const SCHEMA_SQL = ` INSERT INTO chunks_fts(chunks_fts, rowid, content) VALUES ('delete', old.id, old.content); INSERT INTO chunks_fts(rowid, content) VALUES (new.id, new.content); END; + + CREATE TABLE IF NOT EXISTS traul_meta ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ); `; export function initializeDatabase(path: string): Database { diff --git a/test/db/schema.test.ts b/test/db/schema.test.ts index 68e203b..d67847d 100644 --- a/test/db/schema.test.ts +++ b/test/db/schema.test.ts @@ -45,4 +45,17 @@ describe("initializeDatabase", () => { expect(() => initializeDatabase(":memory:")).not.toThrow(); db.close(); }); + + it("creates traul_meta table", () => { + const db = initializeDatabase(":memory:"); + const tables = db + .query<{ name: string }, []>( + "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name" + ) + .all() + .map((r) => r.name); + + expect(tables).toContain("traul_meta"); + db.close(); + }); }); From 0c970782849d0c3b219b166b7fe6dd00ba5b9153 Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 21:58:56 +0000 Subject: [PATCH 2/9] feat: export CHUNKER_VERSION constant Co-Authored-By: Claude Sonnet 4.6 --- src/lib/chunker.ts | 1 + test/lib/chunker.test.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/chunker.ts b/src/lib/chunker.ts index 8e4e2ec..83485f4 100644 --- a/src/lib/chunker.ts +++ b/src/lib/chunker.ts @@ -13,6 +13,7 @@ export interface Chunk { const DEFAULT_CHUNK_SIZE = 1500; const DEFAULT_OVERLAP = 200; export const CHUNK_THRESHOLD = 2000; +export const CHUNKER_VERSION = "1"; export function shouldChunk(text: string, threshold: number = CHUNK_THRESHOLD): boolean { return text.length > threshold; diff --git a/test/lib/chunker.test.ts b/test/lib/chunker.test.ts index a0a1d09..f680ac5 100644 --- a/test/lib/chunker.test.ts +++ b/test/lib/chunker.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "bun:test"; -import { chunkText, shouldChunk } from "../../src/lib/chunker"; +import { chunkText, shouldChunk, CHUNKER_VERSION } from "../../src/lib/chunker"; describe("shouldChunk", () => { it("returns false for short text", () => { @@ -74,3 +74,10 @@ describe("chunkText", () => { } }); }); + +describe("CHUNKER_VERSION", () => { + it("exports a version string", () => { + expect(typeof CHUNKER_VERSION).toBe("string"); + expect(CHUNKER_VERSION.length).toBeGreaterThan(0); + }); +}); From 4d353560fe131e30b98e62bef837906d20b92598 Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 21:59:48 +0000 Subject: [PATCH 3/9] feat: add getMeta/setMeta for version tracking Co-Authored-By: Claude Sonnet 4.6 --- src/db/database.ts | 16 ++++++++++++++++ test/db/database.test.ts | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/db/database.ts b/src/db/database.ts index a08e9cb..decc802 100644 --- a/src/db/database.ts +++ b/src/db/database.ts @@ -817,6 +817,22 @@ export class TraulDB { this.db.run("DELETE FROM sync_cursors WHERE source = ? AND key = ?", [source, key]); } + getMeta(key: string): string | null { + const row = this.db + .query<{ value: string }, [string]>( + "SELECT value FROM traul_meta WHERE key = ?" + ) + .get(key); + return row?.value ?? null; + } + + setMeta(key: string, value: string): void { + this.db.run( + "INSERT INTO traul_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value", + [key, value] + ); + } + close(): void { this.db.close(); } diff --git a/test/db/database.test.ts b/test/db/database.test.ts index ccfe95c..aa7dd03 100644 --- a/test/db/database.test.ts +++ b/test/db/database.test.ts @@ -268,6 +268,23 @@ describe("TraulDB", () => { }); }); + describe("meta", () => { + it("returns null for missing key", () => { + expect(db.getMeta("nonexistent")).toBeNull(); + }); + + it("stores and retrieves a value", () => { + db.setMeta("chunker_version", "1"); + expect(db.getMeta("chunker_version")).toBe("1"); + }); + + it("overwrites existing value", () => { + db.setMeta("chunker_version", "1"); + db.setMeta("chunker_version", "2"); + expect(db.getMeta("chunker_version")).toBe("2"); + }); + }); + describe("stats", () => { it("returns correct counts", () => { db.upsertMessage({ From aa84ee67066e8791a9fbe0c573d06d40b0c5b17c Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 22:01:06 +0000 Subject: [PATCH 4/9] feat: add resetSyncCursors and resetChunks methods Co-Authored-By: Claude Sonnet 4.6 --- src/db/database.ts | 13 ++++++++ test/db/database.test.ts | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/src/db/database.ts b/src/db/database.ts index decc802..b7c4369 100644 --- a/src/db/database.ts +++ b/src/db/database.ts @@ -817,6 +817,19 @@ export class TraulDB { this.db.run("DELETE FROM sync_cursors WHERE source = ? AND key = ?", [source, key]); } + resetSyncCursors(source?: string): void { + if (source) { + this.db.run("DELETE FROM sync_cursors WHERE source = ?", [source]); + } else { + this.db.run("DELETE FROM sync_cursors"); + } + } + + resetChunks(): void { + this.db.run("DELETE FROM vec_chunks"); + this.db.run("DELETE FROM chunks"); + } + getMeta(key: string): string | null { const row = this.db .query<{ value: string }, [string]>( diff --git a/test/db/database.test.ts b/test/db/database.test.ts index aa7dd03..f6521d5 100644 --- a/test/db/database.test.ts +++ b/test/db/database.test.ts @@ -268,6 +268,76 @@ describe("TraulDB", () => { }); }); + describe("resetSyncCursors", () => { + it("clears all cursors for a source", () => { + db.setSyncCursor("markdown", "file:a.md", "hash1"); + db.setSyncCursor("markdown", "file:b.md", "hash2"); + db.setSyncCursor("slack", "channel:C1", "ts1"); + + db.resetSyncCursors("markdown"); + + expect(db.getSyncCursor("markdown", "file:a.md")).toBeNull(); + expect(db.getSyncCursor("markdown", "file:b.md")).toBeNull(); + expect(db.getSyncCursor("slack", "channel:C1")).toBe("ts1"); + }); + + it("clears all cursors when no source given", () => { + db.setSyncCursor("markdown", "file:a.md", "hash1"); + db.setSyncCursor("slack", "channel:C1", "ts1"); + + db.resetSyncCursors(); + + expect(db.getSyncCursor("markdown", "file:a.md")).toBeNull(); + expect(db.getSyncCursor("slack", "channel:C1")).toBeNull(); + }); + }); + + describe("resetChunks", () => { + it("deletes all chunks and their embeddings", () => { + db.upsertMessage({ + source: "markdown", + source_id: "md:abc", + channel_name: "notes", + author_name: "doc", + content: "x".repeat(3000), + sent_at: 1700000000, + }); + + const msg = db.db + .query<{ id: number }, [string]>("SELECT id FROM messages WHERE source_id = ?") + .get("md:abc"); + + db.replaceChunks(msg!.id, [ + { index: 0, content: "chunk 0", embeddingInput: "chunk 0" }, + { index: 1, content: "chunk 1", embeddingInput: "chunk 1" }, + ]); + + const chunksBefore = db.getChunkEmbeddingStats(); + expect(chunksBefore.total_chunks).toBe(2); + + db.resetChunks(); + + const chunksAfter = db.getChunkEmbeddingStats(); + expect(chunksAfter.total_chunks).toBe(0); + }); + + it("does not delete messages", () => { + db.upsertMessage({ + source: "markdown", + source_id: "md:abc", + channel_name: "notes", + author_name: "doc", + content: "some content", + sent_at: 1700000000, + }); + + db.resetChunks(); + + const stats = db.getStats(); + expect(stats.total_messages).toBe(1); + }); + }); + describe("meta", () => { it("returns null for missing key", () => { expect(db.getMeta("nonexistent")).toBeNull(); From d660d54ec2f4af8a8ebd7dcea31bac03c0623448 Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 22:02:45 +0000 Subject: [PATCH 5/9] feat: add auto-migration for chunker/embed version changes Co-Authored-By: Claude Sonnet 4.6 --- src/db/migrations.ts | 57 +++++++++++++++++++++ test/db/migrations.test.ts | 101 +++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 src/db/migrations.ts create mode 100644 test/db/migrations.test.ts diff --git a/src/db/migrations.ts b/src/db/migrations.ts new file mode 100644 index 0000000..01af321 --- /dev/null +++ b/src/db/migrations.ts @@ -0,0 +1,57 @@ +import type { TraulDB } from "./database"; +import { CHUNKER_VERSION } from "../lib/chunker"; +import { EMBED_MODEL, EMBED_DIMS } from "../lib/embeddings"; +import * as log from "../lib/logger"; + +export interface MigrationResult { + chunksReset: boolean; + embeddingsReset: boolean; + syncCursorsReset: boolean; +} + +export function runMigrations(db: TraulDB): MigrationResult { + const result: MigrationResult = { + chunksReset: false, + embeddingsReset: false, + syncCursorsReset: false, + }; + + const storedChunkerVersion = db.getMeta("chunker_version"); + const storedEmbedModel = db.getMeta("embed_model"); + const storedEmbedDims = db.getMeta("embed_dims"); + + const currentDims = String(EMBED_DIMS); + + // Chunker version change → reset chunks + embeddings + markdown cursors + if (storedChunkerVersion !== null && storedChunkerVersion !== CHUNKER_VERSION) { + log.info(`Chunker updated (v${storedChunkerVersion} → v${CHUNKER_VERSION}), rechunking on next sync...`); + db.resetChunks(); + db.resetEmbeddings(EMBED_DIMS); + db.resetSyncCursors("markdown"); + result.chunksReset = true; + result.embeddingsReset = true; + result.syncCursorsReset = true; + } + + // Embed model or dims change → reset embeddings only + if ( + !result.embeddingsReset && + storedEmbedModel !== null && + (storedEmbedModel !== EMBED_MODEL || storedEmbedDims !== currentDims) + ) { + const reason = + storedEmbedModel !== EMBED_MODEL + ? `model changed (${storedEmbedModel} → ${EMBED_MODEL})` + : `dimensions changed (${storedEmbedDims} → ${currentDims})`; + log.info(`Embedding ${reason}, re-embed with 'traul embed'...`); + db.resetEmbeddings(EMBED_DIMS); + result.embeddingsReset = true; + } + + // Update stored values + db.setMeta("chunker_version", CHUNKER_VERSION); + db.setMeta("embed_model", EMBED_MODEL); + db.setMeta("embed_dims", currentDims); + + return result; +} diff --git a/test/db/migrations.test.ts b/test/db/migrations.test.ts new file mode 100644 index 0000000..380dbb7 --- /dev/null +++ b/test/db/migrations.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect, beforeEach } from "bun:test"; +import { TraulDB } from "../../src/db/database"; +import { runMigrations, type MigrationResult } from "../../src/db/migrations"; +import { CHUNKER_VERSION } from "../../src/lib/chunker"; +import { EMBED_MODEL, EMBED_DIMS } from "../../src/lib/embeddings"; + +describe("runMigrations", () => { + let db: TraulDB; + + beforeEach(() => { + db = new TraulDB(":memory:"); + }); + + it("sets initial meta values on fresh database", () => { + const result = runMigrations(db); + + expect(db.getMeta("chunker_version")).toBe(CHUNKER_VERSION); + expect(db.getMeta("embed_model")).toBe(EMBED_MODEL); + expect(db.getMeta("embed_dims")).toBe(String(EMBED_DIMS)); + expect(result.chunksReset).toBe(false); + expect(result.embeddingsReset).toBe(false); + expect(result.syncCursorsReset).toBe(false); + }); + + it("resets chunks when chunker_version changes", () => { + db.setMeta("chunker_version", "0"); + db.setMeta("embed_model", EMBED_MODEL); + db.setMeta("embed_dims", String(EMBED_DIMS)); + + db.upsertMessage({ + source: "markdown", + source_id: "md:test", + channel_name: "notes", + author_name: "doc", + content: "x".repeat(3000), + sent_at: 1700000000, + }); + const msg = db.db + .query<{ id: number }, [string]>("SELECT id FROM messages WHERE source_id = ?") + .get("md:test"); + db.replaceChunks(msg!.id, [ + { index: 0, content: "old chunk", embeddingInput: "old chunk" }, + ]); + db.setSyncCursor("markdown", "file:test.md", "oldhash"); + + const result = runMigrations(db); + + expect(result.chunksReset).toBe(true); + expect(result.embeddingsReset).toBe(true); + expect(result.syncCursorsReset).toBe(true); + expect(db.getChunkEmbeddingStats().total_chunks).toBe(0); + expect(db.getEmbeddingStats().embedded_messages).toBe(0); + expect(db.getSyncCursor("markdown", "file:test.md")).toBeNull(); + }); + + it("resets embeddings when embed_model changes", () => { + db.setMeta("chunker_version", CHUNKER_VERSION); + db.setMeta("embed_model", "old-model"); + db.setMeta("embed_dims", String(EMBED_DIMS)); + + const result = runMigrations(db); + + expect(result.embeddingsReset).toBe(true); + expect(result.chunksReset).toBe(false); + expect(db.getMeta("embed_model")).toBe(EMBED_MODEL); + expect(db.getEmbeddingStats().embedded_messages).toBe(0); + }); + + it("resets embeddings when embed_dims changes", () => { + db.setMeta("chunker_version", CHUNKER_VERSION); + db.setMeta("embed_model", EMBED_MODEL); + db.setMeta("embed_dims", "512"); + + const result = runMigrations(db); + + expect(result.embeddingsReset).toBe(true); + expect(result.chunksReset).toBe(false); + expect(db.getEmbeddingStats().embedded_messages).toBe(0); + }); + + it("does nothing when all versions match", () => { + runMigrations(db); + + db.upsertMessage({ + source: "slack", + source_id: "C1:1", + channel_name: "eng", + author_name: "bob", + content: "hello", + sent_at: 1700000000, + }); + db.setSyncCursor("slack", "channel:C1", "ts1"); + + const result = runMigrations(db); + + expect(result.chunksReset).toBe(false); + expect(result.embeddingsReset).toBe(false); + expect(result.syncCursorsReset).toBe(false); + expect(db.getSyncCursor("slack", "channel:C1")).toBe("ts1"); + }); +}); From b5ec39c0da0d3c8126e80b81e5071aa11b6908aa Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 22:03:27 +0000 Subject: [PATCH 6/9] feat: run auto-migration on startup --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index 9ce309a..277cd52 100755 --- a/src/index.ts +++ b/src/index.ts @@ -13,10 +13,12 @@ import { runWhatsAppAuth } from "./commands/whatsapp-auth"; import { runDaemonStart, runDaemonStop, runDaemonStatus } from "./commands/daemon"; import { runSql, runSchema } from "./commands/sql"; import { runGet } from "./commands/get"; +import { runMigrations } from "./db/migrations"; const config = loadConfig(); ensureDbDir(config.database.path); const db = new TraulDB(config.database.path); +runMigrations(db); const program = new Command(); From abf14d0e2ff2d089eeec5872c58bdf2ff412e666 Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 22:05:19 +0000 Subject: [PATCH 7/9] feat: add traul reset command for manual data layer resets Adds `traul reset ` (sync, chunks, embed, all) with optional --source filter for sync layer. Deprecates the old `reset-embed` command. Co-Authored-By: Claude Sonnet 4.6 --- src/commands/reset.ts | 36 ++++++++++++++++++ src/index.ts | 19 +++++++--- test/commands/reset.test.ts | 75 +++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/commands/reset.ts create mode 100644 test/commands/reset.test.ts diff --git a/src/commands/reset.ts b/src/commands/reset.ts new file mode 100644 index 0000000..6073519 --- /dev/null +++ b/src/commands/reset.ts @@ -0,0 +1,36 @@ +import type { TraulDB } from "../db/database"; +import { EMBED_DIMS } from "../lib/embeddings"; + +type Layer = "sync" | "chunks" | "embed" | "all"; + +const VALID_LAYERS: Layer[] = ["sync", "chunks", "embed", "all"]; + +export function runReset( + db: TraulDB, + layer: string, + options: { source?: string } +): void { + if (!VALID_LAYERS.includes(layer as Layer)) { + throw new Error(`Unknown layer: ${layer}. Valid layers: ${VALID_LAYERS.join(", ")}`); + } + + const doSync = layer === "sync" || layer === "all"; + const doChunks = layer === "chunks" || layer === "all"; + const doEmbed = layer === "embed" || layer === "all" || layer === "chunks"; + + if (doSync) { + db.resetSyncCursors(options.source); + const scope = options.source ? `${options.source} sync cursors` : "all sync cursors"; + console.log(`Reset ${scope}. Run 'traul sync' to refetch.`); + } + + if (doChunks) { + db.resetChunks(); + console.log("Reset all chunks. They will be regenerated on next 'traul sync' or 'traul embed'."); + } + + if (doEmbed) { + db.resetEmbeddings(EMBED_DIMS); + console.log("Reset all embeddings. Run 'traul embed' to regenerate."); + } +} diff --git a/src/index.ts b/src/index.ts index 277cd52..5f4a893 100755 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import { runWhatsAppAuth } from "./commands/whatsapp-auth"; import { runDaemonStart, runDaemonStop, runDaemonStatus } from "./commands/daemon"; import { runSql, runSchema } from "./commands/sql"; import { runGet } from "./commands/get"; +import { runReset } from "./commands/reset"; import { runMigrations } from "./db/migrations"; const config = loadConfig(); @@ -139,14 +140,22 @@ program db.close(); }); +program + .command("reset") + .description("Reset a data layer (sync, chunks, embed, all)") + .argument("", "layer to reset: sync, chunks, embed, all") + .option("-s, --source ", "filter by source (for sync layer)") + .action(async (layer: string, options) => { + runReset(db, layer, options); + db.close(); + }); + program .command("reset-embed") - .description("Drop all embeddings and recreate vec tables (run 'embed' after to regenerate)") + .description("(deprecated: use 'traul reset embed') Drop all embeddings") .action(async () => { - const { EMBED_DIMS } = await import("./lib/embeddings"); - console.log(`Resetting vec tables to ${EMBED_DIMS} dimensions...`); - db.resetEmbeddings(EMBED_DIMS); - console.log("Done. Run 'traul embed' to regenerate embeddings."); + console.log("Note: 'reset-embed' is deprecated, use 'traul reset embed' instead."); + runReset(db, "embed", {}); db.close(); }); diff --git a/test/commands/reset.test.ts b/test/commands/reset.test.ts new file mode 100644 index 0000000..f497bbe --- /dev/null +++ b/test/commands/reset.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect, beforeEach } from "bun:test"; +import { TraulDB } from "../../src/db/database"; +import { runReset } from "../../src/commands/reset"; + +describe("runReset", () => { + let db: TraulDB; + + beforeEach(() => { + db = new TraulDB(":memory:"); + // Seed data + db.upsertMessage({ + source: "slack", + source_id: "C1:1", + channel_name: "eng", + author_name: "bob", + content: "hello", + sent_at: 1700000000, + }); + db.upsertMessage({ + source: "markdown", + source_id: "md:abc", + channel_name: "notes", + author_name: "doc", + content: "x".repeat(3000), + sent_at: 1700000001, + }); + const msg = db.db + .query<{ id: number }, [string]>("SELECT id FROM messages WHERE source_id = ?") + .get("md:abc"); + db.replaceChunks(msg!.id, [ + { index: 0, content: "chunk 0", embeddingInput: "chunk 0" }, + ]); + db.setSyncCursor("slack", "channel:C1", "ts1"); + db.setSyncCursor("markdown", "file:a.md", "hash1"); + }); + + it("reset sync clears all cursors", () => { + runReset(db, "sync", {}); + expect(db.getSyncCursor("slack", "channel:C1")).toBeNull(); + expect(db.getSyncCursor("markdown", "file:a.md")).toBeNull(); + }); + + it("reset sync with --source filters by source", () => { + runReset(db, "sync", { source: "markdown" }); + expect(db.getSyncCursor("markdown", "file:a.md")).toBeNull(); + expect(db.getSyncCursor("slack", "channel:C1")).toBe("ts1"); + }); + + it("reset chunks deletes chunks and resets embeddings", () => { + runReset(db, "chunks", {}); + expect(db.getChunkEmbeddingStats().total_chunks).toBe(0); + expect(db.getEmbeddingStats().embedded_messages).toBe(0); + }); + + it("reset embed drops vec tables", () => { + runReset(db, "embed", {}); + expect(db.getEmbeddingStats().embedded_messages).toBe(0); + }); + + it("reset all clears everything", () => { + runReset(db, "all", {}); + expect(db.getSyncCursor("slack", "channel:C1")).toBeNull(); + expect(db.getChunkEmbeddingStats().total_chunks).toBe(0); + expect(db.getEmbeddingStats().embedded_messages).toBe(0); + }); + + it("preserves messages on all reset layers", () => { + runReset(db, "all", {}); + expect(db.getStats().total_messages).toBe(2); + }); + + it("throws on invalid layer", () => { + expect(() => runReset(db, "invalid", {})).toThrow("Unknown layer"); + }); +}); From 4271e9fc6e20264f58e6e8e6f7e2c80d43627365 Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 22:05:53 +0000 Subject: [PATCH 8/9] docs: document traul reset command and auto-migration --- skill.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/skill.md b/skill.md index ba309d0..9969db6 100644 --- a/skill.md +++ b/skill.md @@ -163,6 +163,19 @@ Structured overview with three sections: 2. **Stats** — total messages, channels, contacts, active signals 3. **Volume** — last 7 days message bar chart +### `traul reset` + +Reset a data layer to force regeneration. Useful when you need to re-sync, re-chunk, or re-embed data. + +| Subcommand | Description | +|------------|-------------| +| `traul reset sync [--source ]` | Clear sync cursors; full refetch on next sync. Optional `--source` flag filters to a specific connector (e.g., `markdown`, `slack`). | +| `traul reset chunks` | Delete all chunks and embeddings; rechunk on next sync. | +| `traul reset embed` | Drop and recreate vector tables; re-embed with `traul embed`. | +| `traul reset all` | Reset everything: sync cursors + chunks + embeddings. | + +**Auto-migration:** Traul automatically detects version changes on startup. If the chunking algorithm or embedding model/dimensions change between versions, affected data layers are reset automatically. No manual action needed after upgrading. + ### Global Options | Option | Description | From 5593df475061932e07a524bb2944d9f34cc0d374 Mon Sep 17 00:00:00 2001 From: Vlad Ra Date: Wed, 18 Mar 2026 22:07:00 +0000 Subject: [PATCH 9/9] chore: bump version to 0.2.0 --- package.json | 2 +- src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 935654e..877a453 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "traul", - "version": "0.1.0", + "version": "0.2.0", "description": "Personal Intelligence Engine — watches communication streams, identifies patterns, surfaces actionable insights", "license": "AGPL-3.0-only", "repository": { diff --git a/src/index.ts b/src/index.ts index 5f4a893..d2a23b0 100755 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,7 @@ const program = new Command(); program .name("traul") .description("Traul — Personal Intelligence Engine") - .version("0.1.0") + .version("0.2.0") .option("-v, --verbose", "enable verbose output") .hook("preAction", () => { if (program.opts().verbose) {