From 2075ee75c1c9a5442ede2c99de105044413c1966 Mon Sep 17 00:00:00 2001 From: dh Date: Wed, 21 Jan 2026 21:39:35 +0100 Subject: [PATCH 1/2] Enhance Markdown export functionality in MarkdownSerializer - Introduced MarkdownExportOptions interface to allow custom export names and descriptions. - Updated toMarkdown method to accept options for name and description, defaulting to 'MindCache Export'. - Modified exported Markdown structure to conditionally display entry attributes (type, system tags, z-index) based on their values. - Updated tests to validate new export format and ensure default values are omitted when not applicable. - Adjusted import functionality to recognize new Markdown format with 'Keys & Values' section. This change improves the flexibility and clarity of Markdown exports, aligning with user needs for customized documentation. --- .../mindcache/src/core/MarkdownSerializer.ts | 48 +++++++++++--- packages/mindcache/src/core/MindCache.ts | 5 +- .../src/core/mindcache.markdown.test.ts | 65 +++++++++++++++++-- 3 files changed, 102 insertions(+), 16 deletions(-) diff --git a/packages/mindcache/src/core/MarkdownSerializer.ts b/packages/mindcache/src/core/MarkdownSerializer.ts index df70424..89c7ee2 100644 --- a/packages/mindcache/src/core/MarkdownSerializer.ts +++ b/packages/mindcache/src/core/MarkdownSerializer.ts @@ -14,6 +14,16 @@ export interface IMarkdownSerializable { clear(): void; } +/** + * Options for markdown export + */ +export interface MarkdownExportOptions { + /** Name/title for the export (defaults to 'MindCache Export') */ + name?: string; + /** Description to include below the title */ + description?: string; +} + /** * Serializes and deserializes MindCache data to/from Markdown format. */ @@ -21,7 +31,7 @@ export class MarkdownSerializer { /** * Export MindCache data to Markdown format. */ - static toMarkdown(mc: IMarkdownSerializable): string { + static toMarkdown(mc: IMarkdownSerializable, options?: MarkdownExportOptions): string { const now = new Date(); const lines: string[] = []; const appendixEntries: Array<{ @@ -33,13 +43,18 @@ export class MarkdownSerializer { }> = []; let appendixCounter = 0; - lines.push('# MindCache STM Export'); + const name = options?.name || 'MindCache Export'; + lines.push(`# ${name}`); lines.push(''); + if (options?.description) { + lines.push(options.description); + lines.push(''); + } lines.push(`Export Date: ${now.toISOString().split('T')[0]}`); lines.push(''); lines.push('---'); lines.push(''); - lines.push('## STM Entries'); + lines.push('## Keys & Values'); lines.push(''); const sortedKeys = mc.getSortedKeys(); @@ -49,9 +64,20 @@ export class MarkdownSerializer { lines.push(`### ${key}`); const entryType = attributes?.type || 'text'; - lines.push(`- **Type**: \`${entryType}\``); - lines.push(`- **System Tags**: \`${attributes?.systemTags?.join(', ') || 'none'}\``); - lines.push(`- **Z-Index**: \`${attributes?.zIndex ?? 0}\``); + // Only show type if not 'text' (the default) + if (entryType !== 'text') { + lines.push(`- **Type**: \`${entryType}\``); + } + // Only show system tags if there are any + const systemTags = attributes?.systemTags; + if (systemTags && systemTags.length > 0) { + lines.push(`- **System Tags**: \`${systemTags.join(', ')}\``); + } + // Only show z-index if non-zero + const zIndex = attributes?.zIndex ?? 0; + if (zIndex !== 0) { + lines.push(`- **Z-Index**: \`${zIndex}\``); + } if (attributes?.contentTags && attributes.contentTags.length > 0) { lines.push(`- **Tags**: \`${attributes.contentTags.join('`, `')}\``); @@ -278,7 +304,10 @@ export class MarkdownSerializer { // Check if we parsed any keys const hasParsedKeys = lines.some(line => line.startsWith('### ') && !line.startsWith('### Appendix')); - const isSTMExport = markdown.includes('# MindCache STM Export') || markdown.includes('## STM Entries'); + const isSTMExport = markdown.includes('# MindCache STM Export') || + markdown.includes('## STM Entries') || + markdown.includes('## Keys & Values') || + markdown.includes('# MindCache Export'); if (!hasParsedKeys && !isSTMExport && markdown.trim().length > 0) { mc.set_value('imported_content', markdown.trim(), { @@ -416,7 +445,10 @@ export class MarkdownSerializer { // Handle unstructured content const hasParsedKeys = lines.some(line => line.startsWith('### ') && !line.startsWith('### Appendix')); - const isSTMExport = markdown.includes('# MindCache STM Export') || markdown.includes('## STM Entries'); + const isSTMExport = markdown.includes('# MindCache STM Export') || + markdown.includes('## STM Entries') || + markdown.includes('## Keys & Values') || + markdown.includes('# MindCache Export'); if (!hasParsedKeys && !isSTMExport && markdown.trim().length > 0) { result['imported_content'] = { diff --git a/packages/mindcache/src/core/MindCache.ts b/packages/mindcache/src/core/MindCache.ts index 7c23cf1..106667a 100644 --- a/packages/mindcache/src/core/MindCache.ts +++ b/packages/mindcache/src/core/MindCache.ts @@ -1495,9 +1495,10 @@ export class MindCache { /** * Export to Markdown format. + * @param options Optional name and description for the export */ - toMarkdown(): string { - return MarkdownSerializer.toMarkdown(this); + toMarkdown(options?: { name?: string; description?: string }): string { + return MarkdownSerializer.toMarkdown(this, options); } /** diff --git a/packages/mindcache/src/core/mindcache.markdown.test.ts b/packages/mindcache/src/core/mindcache.markdown.test.ts index 71d59f9..5580d99 100644 --- a/packages/mindcache/src/core/mindcache.markdown.test.ts +++ b/packages/mindcache/src/core/mindcache.markdown.test.ts @@ -12,24 +12,49 @@ describe('MindCache Markdown Serialization', () => { test('should export empty STM to markdown', () => { const markdown = cache.toMarkdown(); - expect(markdown).toContain('# MindCache STM Export'); - expect(markdown).toContain('## STM Entries'); + expect(markdown).toContain('# MindCache Export'); + expect(markdown).toContain('## Keys & Values'); // No entries when empty }); - test('should export text values to markdown', () => { + test('should export with custom name and description', () => { + cache.set_value('test', 'value'); + const markdown = cache.toMarkdown({ + name: 'My Project', + description: 'This is my project description.' + }); + + expect(markdown).toContain('# My Project'); + expect(markdown).toContain('This is my project description.'); + expect(markdown).toContain('## Keys & Values'); + }); + + test('should export text values to markdown without type line (default)', () => { cache.set_value('username', 'john_doe'); cache.set_value('email', 'john@example.com'); const markdown = cache.toMarkdown(); expect(markdown).toContain('### username'); - expect(markdown).toContain('- **Type**: `text`'); + // Type 'text' should NOT be shown (it's the default) + expect(markdown).not.toContain('- **Type**: `text`'); expect(markdown).toContain('```\njohn_doe\n```'); expect(markdown).toContain('### email'); expect(markdown).toContain('```\njohn@example.com\n```'); }); + test('should skip default values (z-index 0, no system tags)', () => { + cache.set_value('simple', 'value'); + + const markdown = cache.toMarkdown(); + + expect(markdown).toContain('### simple'); + // These defaults should NOT appear + expect(markdown).not.toContain('- **Type**: `text`'); + expect(markdown).not.toContain('- **System Tags**: `none`'); + expect(markdown).not.toContain('- **Z-Index**: `0`'); + }); + test('should export multiline text with code blocks', () => { cache.set_value('description', 'Line 1\nLine 2\nLine 3'); @@ -53,16 +78,18 @@ describe('MindCache Markdown Serialization', () => { expect(markdown).toContain('"dark"'); }); - test('should export all attributes', () => { + test('should export non-default attributes', () => { cache.set_value('test_key', 'value', { systemTags: ['ApplyTemplate'], - contentTags: ['tag1', 'tag2', 'tag3'] + contentTags: ['tag1', 'tag2', 'tag3'], + zIndex: 5 }); const markdown = cache.toMarkdown(); expect(markdown).toContain('- **System Tags**: `ApplyTemplate`'); expect(markdown).toContain('- **Tags**: `tag1`, `tag2`, `tag3`'); + expect(markdown).toContain('- **Z-Index**: `5`'); }); test('should export image to appendix', () => { @@ -150,6 +177,32 @@ Export Date: 2025-10-01 expect(cache.size()).toBe(0); }); + test('should import new format (Keys & Values)', () => { + const markdown = `# My Project + +This is my project. + +Export Date: 2025-10-01 + +--- + +## Keys & Values + +### username +- **Value**: +\`\`\` +john_doe +\`\`\` + +`; + + cache.fromMarkdown(markdown); + + expect(cache.get_value('username')).toBe('john_doe'); + // Should default to type 'text' + expect(cache.get_attributes('username')?.type).toBe('text'); + }); + test('should import text values', () => { const markdown = `# MindCache STM Export From 160b4c324683fb6bdb36684fc74f2d1420867cc0 Mon Sep 17 00:00:00 2001 From: dh Date: Wed, 21 Jan 2026 21:47:38 +0100 Subject: [PATCH 2/2] Update package version to 3.8.0 in package.json and MindCache.ts - Incremented the package version in package.json to 3.8.0 to reflect the latest changes. - Updated the version property in MindCache.ts to match the new package version, ensuring consistency across the codebase. --- packages/mindcache/package.json | 2 +- packages/mindcache/src/core/MindCache.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mindcache/package.json b/packages/mindcache/package.json index 00612ea..85628ac 100644 --- a/packages/mindcache/package.json +++ b/packages/mindcache/package.json @@ -1,6 +1,6 @@ { "name": "mindcache", - "version": "3.7.0", + "version": "3.8.0", "description": "A TypeScript library for managing short-term memory in AI agents through an LLM-friendly key-value repository", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/packages/mindcache/src/core/MindCache.ts b/packages/mindcache/src/core/MindCache.ts index 106667a..3ec8d72 100644 --- a/packages/mindcache/src/core/MindCache.ts +++ b/packages/mindcache/src/core/MindCache.ts @@ -112,7 +112,7 @@ export class MindCache { private globalListeners: GlobalListener[] = []; // Metadata - public readonly version = '3.6.0'; + public readonly version = '3.8.0'; // Internal flag to prevent sync loops when receiving remote updates // (Less critical with Yjs but kept for API compat)