@mmnto/totem 0.16.0 → 0.17.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,39 @@
1
+ export interface ParsedLesson {
2
+ /** Heading text after "## Lesson — " */
3
+ heading: string;
4
+ /** Extracted tags from the **Tags:** line */
5
+ tags: string[];
6
+ /** The lesson body text (after heading + tags line) */
7
+ body: string;
8
+ /** The full raw text of this lesson section (heading through end) */
9
+ raw: string;
10
+ /** 0-based index in the parsed lessons array */
11
+ index: number;
12
+ }
13
+ export interface DriftResult {
14
+ /** The parsed lesson that has orphaned references */
15
+ lesson: ParsedLesson;
16
+ /** File paths referenced in the lesson that no longer exist */
17
+ orphanedRefs: string[];
18
+ }
19
+ /**
20
+ * Parse a lessons.md file into individual lesson entries.
21
+ * Splits on `## Lesson —` headings and extracts tags + body.
22
+ */
23
+ export declare function parseLessonsFile(content: string): ParsedLesson[];
24
+ /**
25
+ * Extract file path references from a lesson body.
26
+ * Looks for backtick-wrapped content that looks like a real file path.
27
+ */
28
+ export declare function extractFileReferences(body: string): string[];
29
+ /**
30
+ * Check parsed lessons for file references that no longer exist on disk.
31
+ * Returns only lessons that have at least one orphaned reference.
32
+ */
33
+ export declare function detectDrift(lessons: ParsedLesson[], projectRoot: string): DriftResult[];
34
+ /**
35
+ * Rewrite lessons.md content, removing lessons at the specified indices.
36
+ * Returns the new file content.
37
+ */
38
+ export declare function rewriteLessonsFile(content: string, indicesToRemove: Set<number>): string;
39
+ //# sourceMappingURL=drift-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift-detector.d.ts","sourceRoot":"","sources":["../src/drift-detector.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,MAAM,EAAE,YAAY,CAAC;IACrB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AA8CD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAyChE;AAID;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAuC5D;AAID;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CAkBvF;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAqBxF"}
@@ -0,0 +1,168 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ // ─── Constants ─────────────────────────────────────────
4
+ /** File extensions considered valid for drift detection */
5
+ const FILE_EXTENSIONS = new Set([
6
+ '.ts',
7
+ '.tsx',
8
+ '.js',
9
+ '.jsx',
10
+ '.mjs',
11
+ '.cjs',
12
+ '.json',
13
+ '.md',
14
+ '.mdx',
15
+ '.yaml',
16
+ '.yml',
17
+ '.toml',
18
+ '.css',
19
+ '.scss',
20
+ '.less',
21
+ '.html',
22
+ '.vue',
23
+ '.svelte',
24
+ '.py',
25
+ '.rs',
26
+ '.go',
27
+ '.java',
28
+ '.kt',
29
+ '.rb',
30
+ '.php',
31
+ '.sql',
32
+ '.graphql',
33
+ '.gql',
34
+ '.sh',
35
+ '.bash',
36
+ '.zsh',
37
+ '.env',
38
+ '.lock',
39
+ '.config',
40
+ ]);
41
+ // ─── Lesson parser ─────────────────────────────────────
42
+ const LESSON_HEADING_RE = /^## Lesson — /m;
43
+ /**
44
+ * Parse a lessons.md file into individual lesson entries.
45
+ * Splits on `## Lesson —` headings and extracts tags + body.
46
+ */
47
+ export function parseLessonsFile(content) {
48
+ const lessons = [];
49
+ // Split on lesson headings, keeping the delimiter
50
+ const parts = content.split(LESSON_HEADING_RE);
51
+ // parts[0] is the file header (before the first lesson)
52
+ for (let i = 1; i < parts.length; i++) {
53
+ const part = parts[i];
54
+ const lines = part.split('\n');
55
+ // First line is the heading (rest of the ## Lesson — line)
56
+ const heading = (lines[0] ?? '').trim();
57
+ // Find tags line: **Tags:** ...
58
+ let tags = [];
59
+ let bodyStartIdx = 1;
60
+ for (let j = 1; j < lines.length; j++) {
61
+ const line = lines[j].trim();
62
+ if (!line)
63
+ continue; // skip blank lines between heading and tags
64
+ if (line.startsWith('**Tags:**')) {
65
+ tags = line
66
+ .replace('**Tags:**', '')
67
+ .split(',')
68
+ .map((t) => t.trim())
69
+ .filter(Boolean);
70
+ bodyStartIdx = j + 1;
71
+ break;
72
+ }
73
+ // If we hit non-empty, non-tags content, no tags line exists
74
+ bodyStartIdx = j;
75
+ break;
76
+ }
77
+ const body = lines.slice(bodyStartIdx).join('\n').trim();
78
+ const raw = `## Lesson — ${part}`;
79
+ lessons.push({ heading, tags, body, raw, index: i - 1 });
80
+ }
81
+ return lessons;
82
+ }
83
+ // ─── File reference extraction ─────────────────────────
84
+ /**
85
+ * Extract file path references from a lesson body.
86
+ * Looks for backtick-wrapped content that looks like a real file path.
87
+ */
88
+ export function extractFileReferences(body) {
89
+ const refs = new Set();
90
+ // Split by code fences and only process content outside them (even-indexed parts)
91
+ const segments = body.split('```');
92
+ for (let i = 0; i < segments.length; i += 2) {
93
+ const segment = segments[i];
94
+ const inlineCodeRe = /(?<!`)`([^`\n]+)`(?!`)/g;
95
+ let match;
96
+ while ((match = inlineCodeRe.exec(segment)) !== null) {
97
+ const candidate = match[1].trim();
98
+ // Must contain a forward slash (path separator)
99
+ if (!candidate.includes('/'))
100
+ continue;
101
+ // Exclude URLs
102
+ if (candidate.startsWith('http://') || candidate.startsWith('https://'))
103
+ continue;
104
+ // Exclude glob patterns
105
+ if (candidate.includes('*') || candidate.includes('?'))
106
+ continue;
107
+ // Exclude npm package names (@scope/name)
108
+ if (candidate.startsWith('@') && !candidate.startsWith('.'))
109
+ continue;
110
+ // Exclude shell commands with flags
111
+ if (candidate.includes(' -') || candidate.includes(' --'))
112
+ continue;
113
+ // Must have a recognized file extension
114
+ const ext = path.extname(candidate).toLowerCase();
115
+ if (!ext || !FILE_EXTENSIONS.has(ext))
116
+ continue;
117
+ // Normalize path separators
118
+ refs.add(candidate.replace(/\\/g, '/'));
119
+ }
120
+ }
121
+ return [...refs];
122
+ }
123
+ // ─── Drift detection ──────────────────────────────────
124
+ /**
125
+ * Check parsed lessons for file references that no longer exist on disk.
126
+ * Returns only lessons that have at least one orphaned reference.
127
+ */
128
+ export function detectDrift(lessons, projectRoot) {
129
+ const results = [];
130
+ for (const lesson of lessons) {
131
+ const refs = extractFileReferences(lesson.body);
132
+ if (refs.length === 0)
133
+ continue;
134
+ const orphaned = refs.filter((ref) => {
135
+ const absPath = path.join(projectRoot, ref);
136
+ return !fs.existsSync(absPath);
137
+ });
138
+ if (orphaned.length > 0) {
139
+ results.push({ lesson, orphanedRefs: orphaned });
140
+ }
141
+ }
142
+ return results;
143
+ }
144
+ // ─── Lesson file rewriting ────────────────────────────
145
+ /**
146
+ * Rewrite lessons.md content, removing lessons at the specified indices.
147
+ * Returns the new file content.
148
+ */
149
+ export function rewriteLessonsFile(content, indicesToRemove) {
150
+ if (indicesToRemove.size === 0)
151
+ return content;
152
+ // Split on lesson headings to preserve the file header
153
+ const parts = content.split(LESSON_HEADING_RE);
154
+ const header = parts[0]; // Everything before the first lesson
155
+ // Reconstruct: header + kept lessons
156
+ const kept = [header];
157
+ for (let i = 1; i < parts.length; i++) {
158
+ if (!indicesToRemove.has(i - 1)) {
159
+ kept.push(`## Lesson — ${parts[i]}`);
160
+ }
161
+ }
162
+ // Join, clean up excessive blank lines, and ensure single trailing newline
163
+ let result = kept.join('');
164
+ result = result.replace(/\n{3,}/g, '\n\n');
165
+ result = result.trimEnd() + '\n';
166
+ return result;
167
+ }
168
+ //# sourceMappingURL=drift-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift-detector.js","sourceRoot":"","sources":["../src/drift-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=drift-detector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift-detector.test.d.ts","sourceRoot":"","sources":["../src/drift-detector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,263 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { detectDrift, extractFileReferences, parseLessonsFile, rewriteLessonsFile, } from './drift-detector.js';
6
+ // ─── parseLessonsFile ─────────────────────────────────
7
+ describe('parseLessonsFile', () => {
8
+ it('parses a standard lessons file with multiple entries', () => {
9
+ const content = `# Totem Lessons
10
+
11
+ Lessons learned from PR reviews and Shield checks.
12
+
13
+ ---
14
+
15
+ ## Lesson — First heading
16
+
17
+ **Tags:** tag1, tag2
18
+
19
+ Body of the first lesson.
20
+
21
+ ## Lesson — Second heading
22
+
23
+ **Tags:** tag3
24
+
25
+ Body of the second lesson with multiple lines.
26
+ And another line.
27
+ `;
28
+ const lessons = parseLessonsFile(content);
29
+ expect(lessons).toHaveLength(2);
30
+ expect(lessons[0].heading).toBe('First heading');
31
+ expect(lessons[0].tags).toEqual(['tag1', 'tag2']);
32
+ expect(lessons[0].body).toBe('Body of the first lesson.');
33
+ expect(lessons[0].index).toBe(0);
34
+ expect(lessons[1].heading).toBe('Second heading');
35
+ expect(lessons[1].tags).toEqual(['tag3']);
36
+ expect(lessons[1].body).toContain('Body of the second lesson');
37
+ expect(lessons[1].body).toContain('And another line.');
38
+ expect(lessons[1].index).toBe(1);
39
+ });
40
+ it('handles lessons with timestamp headings', () => {
41
+ const content = `# Totem Lessons
42
+
43
+ ---
44
+
45
+ ## Lesson — 2026-03-06T03:27:22.818Z
46
+
47
+ **Tags:** lancedb, trap
48
+
49
+ Some lesson body.
50
+ `;
51
+ const lessons = parseLessonsFile(content);
52
+ expect(lessons).toHaveLength(1);
53
+ expect(lessons[0].heading).toBe('2026-03-06T03:27:22.818Z');
54
+ });
55
+ it('returns empty array for file with no lessons', () => {
56
+ const content = `# Totem Lessons
57
+
58
+ Lessons learned from PR reviews.
59
+
60
+ ---
61
+ `;
62
+ const lessons = parseLessonsFile(content);
63
+ expect(lessons).toHaveLength(0);
64
+ });
65
+ it('preserves raw text for reconstruction', () => {
66
+ const content = `# Header
67
+
68
+ ---
69
+
70
+ ## Lesson — Test
71
+
72
+ **Tags:** a
73
+
74
+ Body text.
75
+ `;
76
+ const lessons = parseLessonsFile(content);
77
+ expect(lessons[0].raw).toContain('## Lesson — Test');
78
+ expect(lessons[0].raw).toContain('Body text.');
79
+ });
80
+ });
81
+ // ─── extractFileReferences ────────────────────────────
82
+ describe('extractFileReferences', () => {
83
+ it('extracts backtick-wrapped file paths', () => {
84
+ const body = 'The issue is in `packages/core/src/sync.ts` and also affects `src/utils.ts`.';
85
+ const refs = extractFileReferences(body);
86
+ expect(refs).toEqual(['packages/core/src/sync.ts', 'src/utils.ts']);
87
+ });
88
+ it('ignores non-path backtick content', () => {
89
+ const body = 'Use `totem sync` to re-index. The `filePath` column uses `ENOBUFS` error code.';
90
+ const refs = extractFileReferences(body);
91
+ expect(refs).toHaveLength(0);
92
+ });
93
+ it('ignores URLs', () => {
94
+ const body = 'See `https://example.com/path/file.ts` for details.';
95
+ const refs = extractFileReferences(body);
96
+ expect(refs).toHaveLength(0);
97
+ });
98
+ it('ignores glob patterns', () => {
99
+ const body = 'Add `src/**/*.ts` to your config.';
100
+ const refs = extractFileReferences(body);
101
+ expect(refs).toHaveLength(0);
102
+ });
103
+ it('ignores npm package names', () => {
104
+ const body = 'Import from `@mmnto/totem` package.';
105
+ const refs = extractFileReferences(body);
106
+ expect(refs).toHaveLength(0);
107
+ });
108
+ it('ignores shell commands with flags', () => {
109
+ const body = 'Run `git diff --name-only HEAD~1` to see changes.';
110
+ const refs = extractFileReferences(body);
111
+ expect(refs).toHaveLength(0);
112
+ });
113
+ it('handles dotfile paths', () => {
114
+ const body = 'Check `.totem/lessons.md` for existing entries.';
115
+ const refs = extractFileReferences(body);
116
+ expect(refs).toEqual(['.totem/lessons.md']);
117
+ });
118
+ it('deduplicates references', () => {
119
+ const body = 'The file `src/index.ts` is important. Also see `src/index.ts` again.';
120
+ const refs = extractFileReferences(body);
121
+ expect(refs).toEqual(['src/index.ts']);
122
+ });
123
+ it('ignores content inside code blocks', () => {
124
+ // Code fences use triple backticks — our regex excludes them
125
+ const body = 'The path `src/real.ts` is referenced but ```\n`src/fake.ts`\n``` is in a block.';
126
+ const refs = extractFileReferences(body);
127
+ expect(refs).toContain('src/real.ts');
128
+ expect(refs).not.toContain('src/fake.ts');
129
+ });
130
+ it('handles paths without leading directory', () => {
131
+ const body = 'Edit `config/database.json` to add the new table.';
132
+ const refs = extractFileReferences(body);
133
+ expect(refs).toEqual(['config/database.json']);
134
+ });
135
+ });
136
+ // ─── detectDrift ──────────────────────────────────────
137
+ describe('detectDrift', () => {
138
+ let tmpDir;
139
+ beforeEach(() => {
140
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-drift-'));
141
+ // Create some files
142
+ fs.mkdirSync(path.join(tmpDir, 'src'), { recursive: true });
143
+ fs.writeFileSync(path.join(tmpDir, 'src', 'index.ts'), '');
144
+ fs.writeFileSync(path.join(tmpDir, 'src', 'utils.ts'), '');
145
+ });
146
+ afterEach(() => {
147
+ fs.rmSync(tmpDir, { recursive: true, force: true });
148
+ });
149
+ it('returns empty array when all references exist', () => {
150
+ const lessons = parseLessonsFile(`# Header
151
+
152
+ ---
153
+
154
+ ## Lesson — Test
155
+
156
+ **Tags:** test
157
+
158
+ The file \`src/index.ts\` works fine.
159
+ `);
160
+ const drift = detectDrift(lessons, tmpDir);
161
+ expect(drift).toHaveLength(0);
162
+ });
163
+ it('detects orphaned file references', () => {
164
+ const lessons = parseLessonsFile(`# Header
165
+
166
+ ---
167
+
168
+ ## Lesson — Test
169
+
170
+ **Tags:** test
171
+
172
+ The file \`src/deleted.ts\` was removed.
173
+ `);
174
+ const drift = detectDrift(lessons, tmpDir);
175
+ expect(drift).toHaveLength(1);
176
+ expect(drift[0].orphanedRefs).toEqual(['src/deleted.ts']);
177
+ expect(drift[0].lesson.heading).toBe('Test');
178
+ });
179
+ it('skips lessons with no file references', () => {
180
+ const lessons = parseLessonsFile(`# Header
181
+
182
+ ---
183
+
184
+ ## Lesson — General advice
185
+
186
+ **Tags:** general
187
+
188
+ Always use \`const\` instead of \`let\` when possible.
189
+ `);
190
+ const drift = detectDrift(lessons, tmpDir);
191
+ expect(drift).toHaveLength(0);
192
+ });
193
+ it('reports only orphaned refs, not valid ones', () => {
194
+ const lessons = parseLessonsFile(`# Header
195
+
196
+ ---
197
+
198
+ ## Lesson — Mixed
199
+
200
+ **Tags:** test
201
+
202
+ See \`src/index.ts\` (exists) and \`src/gone.ts\` (deleted).
203
+ `);
204
+ const drift = detectDrift(lessons, tmpDir);
205
+ expect(drift).toHaveLength(1);
206
+ expect(drift[0].orphanedRefs).toEqual(['src/gone.ts']);
207
+ });
208
+ });
209
+ // ─── rewriteLessonsFile ───────────────────────────────
210
+ describe('rewriteLessonsFile', () => {
211
+ const SAMPLE = `# Totem Lessons
212
+
213
+ Lessons learned.
214
+
215
+ ---
216
+
217
+ ## Lesson — First
218
+
219
+ **Tags:** a
220
+
221
+ Body one.
222
+
223
+ ## Lesson — Second
224
+
225
+ **Tags:** b
226
+
227
+ Body two.
228
+
229
+ ## Lesson — Third
230
+
231
+ **Tags:** c
232
+
233
+ Body three.
234
+ `;
235
+ it('removes specified lessons by index', () => {
236
+ const result = rewriteLessonsFile(SAMPLE, new Set([1]));
237
+ expect(result).toContain('## Lesson — First');
238
+ expect(result).not.toContain('## Lesson — Second');
239
+ expect(result).toContain('## Lesson — Third');
240
+ });
241
+ it('removes multiple lessons', () => {
242
+ const result = rewriteLessonsFile(SAMPLE, new Set([0, 2]));
243
+ expect(result).not.toContain('## Lesson — First');
244
+ expect(result).toContain('## Lesson — Second');
245
+ expect(result).not.toContain('## Lesson — Third');
246
+ });
247
+ it('preserves the file header', () => {
248
+ const result = rewriteLessonsFile(SAMPLE, new Set([0, 1, 2]));
249
+ expect(result).toContain('# Totem Lessons');
250
+ expect(result).toContain('Lessons learned.');
251
+ expect(result).not.toContain('## Lesson —');
252
+ });
253
+ it('returns unchanged content when no indices to remove', () => {
254
+ const result = rewriteLessonsFile(SAMPLE, new Set());
255
+ expect(result).toBe(SAMPLE);
256
+ });
257
+ it('ends with a single newline', () => {
258
+ const result = rewriteLessonsFile(SAMPLE, new Set([2]));
259
+ expect(result.endsWith('\n')).toBe(true);
260
+ expect(result.endsWith('\n\n')).toBe(false);
261
+ });
262
+ });
263
+ //# sourceMappingURL=drift-detector.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift-detector.test.js","sourceRoot":"","sources":["../src/drift-detector.test.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@ export { TOTEM_TABLE_NAME } from './store/lance-schema.js';
9
9
  export { LanceStore } from './store/lance-store.js';
10
10
  export type { ResolvedFile } from './ingest/sync.js';
11
11
  export { getChangedFiles, getHeadSha, resolveFiles, runSync } from './ingest/sync.js';
12
+ export type { DriftResult, ParsedLesson } from './drift-detector.js';
13
+ export { detectDrift, extractFileReferences, parseLessonsFile, rewriteLessonsFile, } from './drift-detector.js';
12
14
  export { generateLessonHeading } from './lesson-format.js';
13
15
  export { sanitize } from './sanitize.js';
14
16
  export { wrapXml } from './xml-format.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,UAAU,EACV,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,KAAK,EACL,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACX,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,YAAY,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,UAAU,EACV,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,KAAK,EACL,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACX,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,YAAY,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGtF,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { createEmbedder } from './embedders/embedder.js';
5
5
  export { TOTEM_TABLE_NAME } from './store/lance-schema.js';
6
6
  export { LanceStore } from './store/lance-store.js';
7
7
  export { getChangedFiles, getHeadSha, resolveFiles, runSync } from './ingest/sync.js';
8
+ export { detectDrift, extractFileReferences, parseLessonsFile, rewriteLessonsFile, } from './drift-detector.js';
8
9
  // Utilities
9
10
  export { generateLessonHeading } from './lesson-format.js';
10
11
  export { sanitize } from './sanitize.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAc5B,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,QAAQ;AACR,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEtF,YAAY;AACZ,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAc5B,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,QAAQ;AACR,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAItF,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAE7B,YAAY;AACZ,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmnto/totem",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Persistent memory and context layer for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",