@yeseh/cortex-cli 0.6.8 → 0.6.9

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 (74) hide show
  1. package/dist/program.js +1538 -5
  2. package/dist/program.js.map +32 -3
  3. package/dist/run.d.ts +0 -1
  4. package/dist/run.d.ts.map +1 -1
  5. package/dist/run.js +3 -4
  6. package/dist/run.js.map +3 -3
  7. package/package.json +4 -6
  8. package/dist/chunk-dsfj4baj.js +0 -1543
  9. package/dist/chunk-dsfj4baj.js.map +0 -38
  10. package/src/category/commands/create.spec.ts +0 -139
  11. package/src/category/commands/create.ts +0 -119
  12. package/src/category/index.ts +0 -24
  13. package/src/commands/init.spec.ts +0 -203
  14. package/src/commands/init.ts +0 -301
  15. package/src/context.spec.ts +0 -60
  16. package/src/context.ts +0 -170
  17. package/src/errors.spec.ts +0 -264
  18. package/src/errors.ts +0 -105
  19. package/src/memory/commands/add.spec.ts +0 -169
  20. package/src/memory/commands/add.ts +0 -158
  21. package/src/memory/commands/definitions.spec.ts +0 -80
  22. package/src/memory/commands/list.spec.ts +0 -123
  23. package/src/memory/commands/list.ts +0 -269
  24. package/src/memory/commands/move.spec.ts +0 -85
  25. package/src/memory/commands/move.ts +0 -119
  26. package/src/memory/commands/remove.spec.ts +0 -79
  27. package/src/memory/commands/remove.ts +0 -108
  28. package/src/memory/commands/show.spec.ts +0 -71
  29. package/src/memory/commands/show.ts +0 -165
  30. package/src/memory/commands/test-helpers.spec.ts +0 -127
  31. package/src/memory/commands/update.spec.ts +0 -86
  32. package/src/memory/commands/update.ts +0 -230
  33. package/src/memory/index.spec.ts +0 -59
  34. package/src/memory/index.ts +0 -44
  35. package/src/memory/parsing.spec.ts +0 -105
  36. package/src/memory/parsing.ts +0 -22
  37. package/src/observability.spec.ts +0 -126
  38. package/src/observability.ts +0 -82
  39. package/src/output.spec.ts +0 -835
  40. package/src/output.ts +0 -119
  41. package/src/program.spec.ts +0 -46
  42. package/src/program.ts +0 -75
  43. package/src/run.spec.ts +0 -31
  44. package/src/run.ts +0 -9
  45. package/src/store/commands/add.spec.ts +0 -131
  46. package/src/store/commands/add.ts +0 -231
  47. package/src/store/commands/init.spec.ts +0 -220
  48. package/src/store/commands/init.ts +0 -272
  49. package/src/store/commands/list.spec.ts +0 -175
  50. package/src/store/commands/list.ts +0 -102
  51. package/src/store/commands/prune.spec.ts +0 -120
  52. package/src/store/commands/prune.ts +0 -152
  53. package/src/store/commands/reindexs.spec.ts +0 -94
  54. package/src/store/commands/reindexs.ts +0 -97
  55. package/src/store/commands/remove.spec.ts +0 -97
  56. package/src/store/commands/remove.ts +0 -189
  57. package/src/store/index.spec.ts +0 -60
  58. package/src/store/index.ts +0 -49
  59. package/src/store/utils/resolve-store-name.spec.ts +0 -62
  60. package/src/store/utils/resolve-store-name.ts +0 -79
  61. package/src/test-helpers.spec.ts +0 -430
  62. package/src/tests/cli.integration.spec.ts +0 -1306
  63. package/src/toon.spec.ts +0 -183
  64. package/src/toon.ts +0 -462
  65. package/src/utils/git.spec.ts +0 -95
  66. package/src/utils/git.ts +0 -51
  67. package/src/utils/input.spec.ts +0 -326
  68. package/src/utils/input.ts +0 -150
  69. package/src/utils/paths.spec.ts +0 -235
  70. package/src/utils/paths.ts +0 -75
  71. package/src/utils/prompts.spec.ts +0 -23
  72. package/src/utils/prompts.ts +0 -88
  73. package/src/utils/resolve-default-store.spec.ts +0 -135
  74. package/src/utils/resolve-default-store.ts +0 -74
@@ -1,835 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
- import { serializeOutput } from './output.ts';
3
- import type {
4
- OutputCategory,
5
- OutputInit,
6
- OutputMemory,
7
- OutputStore,
8
- OutputStoreInit,
9
- OutputStoreRegistry,
10
- } from './output.ts';
11
-
12
- // Sample test data
13
- const sampleMemory: OutputMemory = {
14
- path: 'global/persona/test-memory',
15
- metadata: {
16
- createdAt: new Date('2024-01-15T10:00:00Z'),
17
- updatedAt: new Date('2024-01-16T14:30:00Z'),
18
- tags: [
19
- 'test', 'example',
20
- ],
21
- source: 'unit-test',
22
- tokenEstimate: 42,
23
- },
24
- content: 'This is test content.',
25
- };
26
-
27
- const sampleCategory: OutputCategory = {
28
- path: 'global/persona',
29
- memories: [
30
- { path: 'global/persona/mem1', tokenEstimate: 10, summary: 'First memory' },
31
- { path: 'global/persona/mem2', tokenEstimate: 20 },
32
- ],
33
- subcategories: [{ path: 'global/persona/sub1', memoryCount: 5 }],
34
- };
35
-
36
- // Helper to serialize with TOON format
37
- const toonSerialize = <T>(kind: string, value: T) =>
38
- serializeOutput({ kind, value } as Parameters<typeof serializeOutput>[0], 'toon');
39
-
40
- describe('serializeMemoryToon', () => {
41
- it('should encodes memory with all metadata fields', () => {
42
- const result = toonSerialize('memory', sampleMemory);
43
-
44
- expect(result.ok()).toBe(true);
45
- if (result.ok()) {
46
- // TOON outputs YAML-like format with spaces after colons and nested structure
47
- expect(result.value).toContain('path: global/persona/test-memory');
48
- // Timestamps are quoted because they contain colons, nested under metadata
49
- expect(result.value).toContain('createdAt: "2024-01-15T10:00:00.000Z"');
50
- expect(result.value).toContain('updatedAt: "2024-01-16T14:30:00.000Z"');
51
- expect(result.value).toContain('tags[2');
52
- expect(result.value).toContain('source: unit-test');
53
- expect(result.value).toContain('tokenEstimate: 42');
54
- expect(result.value).toContain('content: This is test content.');
55
- }
56
- });
57
-
58
- it('should uses nested structure for metadata (not key folding)', () => {
59
- const memory: OutputMemory = {
60
- path: 'test/path',
61
- metadata: {
62
- createdAt: new Date('2024-01-15T10:00:00Z'),
63
- tags: ['tag1'],
64
- },
65
- content: 'Content',
66
- };
67
- const result = toonSerialize('memory', memory);
68
-
69
- expect(result.ok()).toBe(true);
70
- if (result.ok()) {
71
- // TOON produces nested YAML-like structure, not dotted notation
72
- expect(result.value).toContain('metadata:');
73
- expect(result.value).toContain('createdAt:');
74
- expect(result.value).toContain('tags[1');
75
- }
76
- });
77
-
78
- it('should quotes multiline content', () => {
79
- const memory: OutputMemory = {
80
- path: 'test/multiline',
81
- metadata: {
82
- createdAt: new Date('2024-01-15T10:00:00Z'),
83
- tags: [],
84
- },
85
- content: 'Line 1\nLine 2\nLine 3',
86
- };
87
- const result = toonSerialize('memory', memory);
88
-
89
- expect(result.ok()).toBe(true);
90
- if (result.ok()) {
91
- // Multiline content should be quoted (JSON-style)
92
- expect(result.value).toContain('"Line 1\\nLine 2\\nLine 3"');
93
- }
94
- });
95
-
96
- it('should handles missing optional fields (updatedAt, source, tokenEstimate, expiresAt)', () => {
97
- const memory: OutputMemory = {
98
- path: 'test/minimal',
99
- metadata: {
100
- createdAt: new Date('2024-01-15T10:00:00Z'),
101
- tags: ['minimal'],
102
- // No updatedAt, source, tokenEstimate, expiresAt
103
- },
104
- content: 'Minimal content',
105
- };
106
- const result = toonSerialize('memory', memory);
107
-
108
- expect(result.ok()).toBe(true);
109
- if (result.ok()) {
110
- expect(result.value).toContain('path: test/minimal');
111
- expect(result.value).toContain('createdAt:');
112
- expect(result.value).toContain('content: Minimal content');
113
- // Should NOT contain optional fields that weren't set
114
- expect(result.value).not.toContain('updatedAt');
115
- expect(result.value).not.toContain('source');
116
- expect(result.value).not.toContain('tokenEstimate');
117
- expect(result.value).not.toContain('expiresAt');
118
- }
119
- });
120
-
121
- it('should serializes whitespace path without error (no validation)', () => {
122
- // The output module does NOT validate - it just serializes
123
- const memory: OutputMemory = {
124
- path: ' ', // Whitespace path - serializes as-is
125
- metadata: {
126
- createdAt: new Date('2024-01-15T10:00:00Z'),
127
- tags: [],
128
- },
129
- content: 'Content',
130
- };
131
- const result = toonSerialize('memory', memory);
132
-
133
- // Serialization succeeds - validation is at object construction time
134
- expect(result.ok()).toBe(true);
135
- if (result.ok()) {
136
- expect(result.value).toContain('path:');
137
- }
138
- });
139
-
140
- it('should serializes Invalid Date as SERIALIZE_FAILED', () => {
141
- const memory: OutputMemory = {
142
- path: 'test/path',
143
- metadata: {
144
- createdAt: new Date('invalid date'),
145
- tags: [],
146
- },
147
- content: 'Content',
148
- };
149
- const result = toonSerialize('memory', memory);
150
-
151
- // Invalid Date causes serialization to fail
152
- expect(result.ok()).toBe(false);
153
- if (!result.ok()) {
154
- expect(result.error.code).toBe('SERIALIZE_FAILED');
155
- }
156
- });
157
- });
158
-
159
- describe('serializeCategoryToon', () => {
160
- it('should encodes category with memories and subcategories', () => {
161
- const result = toonSerialize('category', sampleCategory);
162
-
163
- expect(result.ok()).toBe(true);
164
- if (result.ok()) {
165
- expect(result.value).toContain('path: global/persona');
166
- // Memories array with TOON array format
167
- expect(result.value).toContain('memories[2');
168
- // Subcategories use tabular format
169
- expect(result.value).toContain('subcategories[1');
170
- }
171
- });
172
-
173
- it('should uses tabular format for uniform memories array', () => {
174
- // Create a uniform array (all items have same keys)
175
- const category: OutputCategory = {
176
- path: 'test/uniform-memories',
177
- memories: [
178
- { path: 'test/mem1', tokenEstimate: 10 },
179
- { path: 'test/mem2', tokenEstimate: 20 },
180
- ],
181
- subcategories: [],
182
- };
183
- const result = toonSerialize('category', category);
184
-
185
- expect(result.ok()).toBe(true);
186
- if (result.ok()) {
187
- // Tabular format with tab-separated header
188
- expect(result.value).toMatch(/memories\[2\t\]\{path\ttokenEstimate\}:/);
189
- // Should contain memory paths in the rows
190
- expect(result.value).toContain('test/mem1');
191
- expect(result.value).toContain('test/mem2');
192
- }
193
- });
194
-
195
- it('should uses tabular format for subcategories array', () => {
196
- const result = toonSerialize('category', sampleCategory);
197
-
198
- expect(result.ok()).toBe(true);
199
- if (result.ok()) {
200
- // Tabular format for subcategories with tab-separated format
201
- expect(result.value).toMatch(/subcategories\[1\t\]\{path\tmemoryCount\}:/);
202
- expect(result.value).toContain('global/persona/sub1');
203
- expect(result.value).toContain('5'); // memory count
204
- }
205
- });
206
-
207
- it('should handles empty memories array', () => {
208
- const category: OutputCategory = {
209
- path: 'test/empty-memories',
210
- memories: [],
211
- subcategories: [{ path: 'test/sub', memoryCount: 3 }],
212
- };
213
- const result = toonSerialize('category', category);
214
-
215
- expect(result.ok()).toBe(true);
216
- if (result.ok()) {
217
- // Empty arrays in TOON format show count of 0
218
- expect(result.value).toContain('memories[0');
219
- }
220
- });
221
-
222
- it('should handles empty subcategories array', () => {
223
- const category: OutputCategory = {
224
- path: 'test/empty-subcategories',
225
- memories: [{ path: 'test/mem1', tokenEstimate: 10 }],
226
- subcategories: [],
227
- };
228
- const result = toonSerialize('category', category);
229
-
230
- expect(result.ok()).toBe(true);
231
- if (result.ok()) {
232
- // Empty arrays in TOON format show count of 0
233
- expect(result.value).toContain('subcategories[0');
234
- }
235
- });
236
-
237
- it('should serializes empty path without error (no validation)', () => {
238
- // The output module does NOT validate - it just serializes
239
- const category: OutputCategory = {
240
- path: '', // Empty path - serializes as-is
241
- memories: [],
242
- subcategories: [],
243
- };
244
- const result = toonSerialize('category', category);
245
-
246
- // Serialization succeeds - validation is at object construction time
247
- expect(result.ok()).toBe(true);
248
- });
249
- });
250
-
251
- describe('serializeStoreToon', () => {
252
- it('should encodes store with name and path', () => {
253
- const store: OutputStore = {
254
- name: 'my-store',
255
- path: '/data/my-store',
256
- };
257
- const result = toonSerialize('store', store);
258
-
259
- expect(result.ok()).toBe(true);
260
- if (result.ok()) {
261
- // TOON outputs YAML-like format with spaces after colons
262
- expect(result.value).toContain('name: my-store');
263
- expect(result.value).toContain('path: /data/my-store');
264
- }
265
- });
266
-
267
- it('should serializes invalid store name without error (no validation)', () => {
268
- // The output module does NOT validate - it just serializes
269
- const store: OutputStore = {
270
- name: 'Invalid Name!', // Not lowercase slug - serializes as-is
271
- path: '/data/store',
272
- };
273
- const result = toonSerialize('store', store);
274
-
275
- // Serialization succeeds - validation is at object construction time
276
- expect(result.ok()).toBe(true);
277
- if (result.ok()) {
278
- expect(result.value).toContain('name: Invalid Name!');
279
- }
280
- });
281
-
282
- it('should serializes whitespace store path without error (no validation)', () => {
283
- // The output module does NOT validate - it just serializes
284
- const store: OutputStore = {
285
- name: 'valid-name',
286
- path: ' ', // Whitespace path - serializes as-is
287
- };
288
- const result = toonSerialize('store', store);
289
-
290
- // Serialization succeeds - validation is at object construction time
291
- expect(result.ok()).toBe(true);
292
- });
293
- });
294
-
295
- describe('serializeStoreRegistryToon', () => {
296
- it('should encodes registry with multiple stores', () => {
297
- const registry: OutputStoreRegistry = {
298
- stores: [
299
- { name: 'store-one', path: '/data/one' },
300
- { name: 'store-two', path: '/data/two' },
301
- ],
302
- };
303
- const result = toonSerialize('store-registry', registry);
304
-
305
- expect(result.ok()).toBe(true);
306
- if (result.ok()) {
307
- expect(result.value).toContain('stores[2');
308
- expect(result.value).toContain('store-one');
309
- expect(result.value).toContain('store-two');
310
- }
311
- });
312
-
313
- it('should uses tabular format for stores array', () => {
314
- const registry: OutputStoreRegistry = {
315
- stores: [
316
- { name: 'alpha', path: '/a' },
317
- { name: 'beta', path: '/b' },
318
- ],
319
- };
320
- const result = toonSerialize('store-registry', registry);
321
-
322
- expect(result.ok()).toBe(true);
323
- if (result.ok()) {
324
- // Tabular format: header with count and field names (tab-separated)
325
- expect(result.value).toMatch(/stores\[2\t\]\{name\tpath\}:/);
326
- }
327
- });
328
-
329
- it('should handles empty stores array', () => {
330
- const registry: OutputStoreRegistry = {
331
- stores: [],
332
- };
333
- const result = toonSerialize('store-registry', registry);
334
-
335
- expect(result.ok()).toBe(true);
336
- if (result.ok()) {
337
- // Empty arrays in TOON format show count of 0
338
- expect(result.value).toContain('stores[0');
339
- }
340
- });
341
-
342
- it('should serializes invalid store in array without error (no validation)', () => {
343
- // The output module does NOT validate - it just serializes
344
- const registry: OutputStoreRegistry = {
345
- stores: [
346
- { name: 'valid-store', path: '/valid' },
347
- { name: 'INVALID STORE', path: '/invalid' }, // Invalid name - serializes as-is
348
- ],
349
- };
350
- const result = toonSerialize('store-registry', registry);
351
-
352
- // Serialization succeeds - validation is at object construction time
353
- expect(result.ok()).toBe(true);
354
- if (result.ok()) {
355
- expect(result.value).toContain('INVALID STORE');
356
- }
357
- });
358
- });
359
-
360
- describe('serializeStoreInitToon', () => {
361
- it('should encodes store init with path and name', () => {
362
- const storeInit: OutputStoreInit = {
363
- path: '/initialized/store/path',
364
- name: 'my-store',
365
- };
366
- const result = toonSerialize('store-init', storeInit);
367
-
368
- expect(result.ok()).toBe(true);
369
- if (result.ok()) {
370
- // TOON outputs YAML-like format with spaces after colons
371
- expect(result.value).toContain('path: /initialized/store/path');
372
- expect(result.value).toContain('name: my-store');
373
- }
374
- });
375
-
376
- it('should serializes empty path without error (no validation)', () => {
377
- // The output module does NOT validate - it just serializes
378
- const storeInit: OutputStoreInit = {
379
- path: '',
380
- name: 'my-store',
381
- };
382
- const result = toonSerialize('store-init', storeInit);
383
-
384
- // Serialization succeeds - validation is at object construction time
385
- expect(result.ok()).toBe(true);
386
- });
387
- });
388
-
389
- describe('serializeInitToon', () => {
390
- it('should encodes init with path and categories', () => {
391
- const init: OutputInit = {
392
- path: '/project/root',
393
- categories: [
394
- 'persona',
395
- 'project',
396
- 'domain',
397
- ],
398
- };
399
- const result = toonSerialize('init', init);
400
-
401
- expect(result.ok()).toBe(true);
402
- if (result.ok()) {
403
- // TOON outputs YAML-like format with spaces after colons
404
- expect(result.value).toContain('path: /project/root');
405
- expect(result.value).toContain('categories[3');
406
- // Categories array
407
- expect(result.value).toContain('persona');
408
- expect(result.value).toContain('project');
409
- expect(result.value).toContain('domain');
410
- }
411
- });
412
-
413
- it('should handles empty categories array', () => {
414
- const init: OutputInit = {
415
- path: '/project/empty',
416
- categories: [],
417
- };
418
- const result = toonSerialize('init', init);
419
-
420
- expect(result.ok()).toBe(true);
421
- if (result.ok()) {
422
- expect(result.value).toContain('path: /project/empty');
423
- // Empty arrays in TOON format show count of 0
424
- expect(result.value).toContain('categories[0');
425
- }
426
- });
427
-
428
- it('should serializes whitespace path without error (no validation)', () => {
429
- // The output module does NOT validate - it just serializes
430
- const init: OutputInit = {
431
- path: ' ',
432
- categories: ['test'],
433
- };
434
- const result = toonSerialize('init', init);
435
-
436
- // Serialization succeeds - validation is at object construction time
437
- expect(result.ok()).toBe(true);
438
- });
439
- });
440
-
441
- describe('TOON edge cases', () => {
442
- it('should handles special characters in content', () => {
443
- const memory: OutputMemory = {
444
- path: 'test/special-chars',
445
- metadata: {
446
- createdAt: new Date('2024-01-15T10:00:00Z'),
447
- tags: [],
448
- },
449
- content: 'Content with "quotes" and : colons',
450
- };
451
- const result = toonSerialize('memory', memory);
452
-
453
- expect(result.ok()).toBe(true);
454
- if (result.ok()) {
455
- // Content with special chars should be quoted
456
- expect(result.value).toContain('"Content with \\"quotes\\" and : colons"');
457
- }
458
- });
459
-
460
- it('should handles tab characters in strings', () => {
461
- const memory: OutputMemory = {
462
- path: 'test/tabs',
463
- metadata: {
464
- createdAt: new Date('2024-01-15T10:00:00Z'),
465
- tags: [],
466
- },
467
- content: 'Column1\tColumn2\tColumn3',
468
- };
469
- const result = toonSerialize('memory', memory);
470
-
471
- expect(result.ok()).toBe(true);
472
- if (result.ok()) {
473
- // Tab characters should be quoted/escaped
474
- expect(result.value).toContain('"Column1\\tColumn2\\tColumn3"');
475
- }
476
- });
477
-
478
- it('should handles newlines in strings', () => {
479
- const memory: OutputMemory = {
480
- path: 'test/newlines',
481
- metadata: {
482
- createdAt: new Date('2024-01-15T10:00:00Z'),
483
- tags: [],
484
- },
485
- content: 'First line\r\nSecond line',
486
- };
487
- const result = toonSerialize('memory', memory);
488
-
489
- expect(result.ok()).toBe(true);
490
- if (result.ok()) {
491
- // Newlines should be quoted/escaped
492
- expect(result.value).toContain('\\r\\n');
493
- }
494
- });
495
-
496
- it('should handles empty strings', () => {
497
- const memory: OutputMemory = {
498
- path: 'test/empty-content',
499
- metadata: {
500
- createdAt: new Date('2024-01-15T10:00:00Z'),
501
- tags: [],
502
- },
503
- content: '',
504
- };
505
- const result = toonSerialize('memory', memory);
506
-
507
- expect(result.ok()).toBe(true);
508
- if (result.ok()) {
509
- // Empty content should be represented
510
- expect(result.value).toContain('content:');
511
- }
512
- });
513
-
514
- it('should handles unicode characters', () => {
515
- const memory: OutputMemory = {
516
- path: 'test/unicode',
517
- metadata: {
518
- createdAt: new Date('2024-01-15T10:00:00Z'),
519
- tags: [
520
- 'emoji', 'unicode',
521
- ],
522
- },
523
- content: 'Hello, world! Bonjour! Hola!',
524
- };
525
- const result = toonSerialize('memory', memory);
526
-
527
- expect(result.ok()).toBe(true);
528
- if (result.ok()) {
529
- expect(result.value).toContain('Hello, world! Bonjour! Hola!');
530
- }
531
- });
532
-
533
- it('should handles memory with expiresAt field', () => {
534
- const memory: OutputMemory = {
535
- path: 'test/expires',
536
- metadata: {
537
- createdAt: new Date('2024-01-15T10:00:00Z'),
538
- tags: [],
539
- expiresAt: new Date('2025-01-15T10:00:00Z'),
540
- },
541
- content: 'Expiring content',
542
- };
543
- const result = toonSerialize('memory', memory);
544
-
545
- expect(result.ok()).toBe(true);
546
- if (result.ok()) {
547
- // Timestamps are quoted, nested under metadata (not dotted)
548
- expect(result.value).toContain('expiresAt: "2025-01-15T10:00:00.000Z"');
549
- }
550
- });
551
-
552
- it('should handles category memory with summary containing special characters', () => {
553
- const category: OutputCategory = {
554
- path: 'test/special-summary',
555
- memories: [{
556
- path: 'test/mem1',
557
- tokenEstimate: 10,
558
- summary: 'Summary with "quotes" and: colons',
559
- }],
560
- subcategories: [],
561
- };
562
- const result = toonSerialize('category', category);
563
-
564
- expect(result.ok()).toBe(true);
565
- if (result.ok()) {
566
- // Summary with special chars should be quoted
567
- expect(result.value).toContain('Summary with');
568
- }
569
- });
570
-
571
- it('should handles tags array in memory', () => {
572
- const memory: OutputMemory = {
573
- path: 'test/tags',
574
- metadata: {
575
- createdAt: new Date('2024-01-15T10:00:00Z'),
576
- tags: [
577
- 'tag1',
578
- 'tag-two',
579
- 'tag_three',
580
- ],
581
- },
582
- content: 'Tagged content',
583
- };
584
- const result = toonSerialize('memory', memory);
585
-
586
- expect(result.ok()).toBe(true);
587
- if (result.ok()) {
588
- // Nested tags under metadata
589
- expect(result.value).toContain('tags[3');
590
- expect(result.value).toContain('tag1');
591
- expect(result.value).toContain('tag-two');
592
- expect(result.value).toContain('tag_three');
593
- }
594
- });
595
-
596
- it('should handles empty tags array', () => {
597
- const memory: OutputMemory = {
598
- path: 'test/no-tags',
599
- metadata: {
600
- createdAt: new Date('2024-01-15T10:00:00Z'),
601
- tags: [],
602
- },
603
- content: 'No tags content',
604
- };
605
- const result = toonSerialize('memory', memory);
606
-
607
- expect(result.ok()).toBe(true);
608
- if (result.ok()) {
609
- // Empty tags array in TOON format shows count of 0
610
- expect(result.value).toContain('tags[0');
611
- }
612
- });
613
-
614
- it('should handles large token estimate values', () => {
615
- const memory: OutputMemory = {
616
- path: 'test/large-tokens',
617
- metadata: {
618
- createdAt: new Date('2024-01-15T10:00:00Z'),
619
- tags: [],
620
- tokenEstimate: 999999,
621
- },
622
- content: 'Large token estimate',
623
- };
624
- const result = toonSerialize('memory', memory);
625
-
626
- expect(result.ok()).toBe(true);
627
- if (result.ok()) {
628
- // Nested under metadata (not dotted)
629
- expect(result.value).toContain('tokenEstimate: 999999');
630
- }
631
- });
632
-
633
- it('should serializes negative token estimate without error (no validation)', () => {
634
- // The output module does NOT validate - it just serializes
635
- const memory: OutputMemory = {
636
- path: 'test/negative-tokens',
637
- metadata: {
638
- createdAt: new Date('2024-01-15T10:00:00Z'),
639
- tags: [],
640
- tokenEstimate: -5,
641
- },
642
- content: 'Invalid token estimate',
643
- };
644
- const result = toonSerialize('memory', memory);
645
-
646
- // Serialization succeeds - validation is at object construction time
647
- expect(result.ok()).toBe(true);
648
- if (result.ok()) {
649
- expect(result.value).toContain('-5');
650
- }
651
- });
652
-
653
- it('should serializes Infinity token estimate without error (no validation)', () => {
654
- // The output module does NOT validate - it just serializes
655
- const memory: OutputMemory = {
656
- path: 'test/infinite-tokens',
657
- metadata: {
658
- createdAt: new Date('2024-01-15T10:00:00Z'),
659
- tags: [],
660
- tokenEstimate: Number.POSITIVE_INFINITY,
661
- },
662
- content: 'Infinite token estimate',
663
- };
664
- const result = toonSerialize('memory', memory);
665
-
666
- // Serialization succeeds - validation is at object construction time
667
- expect(result.ok()).toBe(true);
668
- });
669
-
670
- it('should serializes invalid updatedAt date as SERIALIZE_FAILED', () => {
671
- const memory: OutputMemory = {
672
- path: 'test/invalid-updated',
673
- metadata: {
674
- createdAt: new Date('2024-01-15T10:00:00Z'),
675
- updatedAt: new Date('not a date'),
676
- tags: [],
677
- },
678
- content: 'Invalid updated date',
679
- };
680
- const result = toonSerialize('memory', memory);
681
-
682
- // Invalid Date causes serialization to fail
683
- expect(result.ok()).toBe(false);
684
- if (!result.ok()) {
685
- expect(result.error.code).toBe('SERIALIZE_FAILED');
686
- }
687
- });
688
-
689
- it('should serializes invalid expiresAt date as SERIALIZE_FAILED', () => {
690
- const memory: OutputMemory = {
691
- path: 'test/invalid-expires',
692
- metadata: {
693
- createdAt: new Date('2024-01-15T10:00:00Z'),
694
- tags: [],
695
- expiresAt: new Date('invalid'),
696
- },
697
- content: 'Invalid expires date',
698
- };
699
- const result = toonSerialize('memory', memory);
700
-
701
- // Invalid Date causes serialization to fail
702
- expect(result.ok()).toBe(false);
703
- if (!result.ok()) {
704
- expect(result.error.code).toBe('SERIALIZE_FAILED');
705
- }
706
- });
707
-
708
- it('should handles subcategory with zero memory count', () => {
709
- const category: OutputCategory = {
710
- path: 'test/zero-count',
711
- memories: [],
712
- subcategories: [{ path: 'test/empty-sub', memoryCount: 0 }],
713
- };
714
- const result = toonSerialize('category', category);
715
-
716
- expect(result.ok()).toBe(true);
717
- if (result.ok()) {
718
- expect(result.value).toContain('test/empty-sub');
719
- expect(result.value).toContain('0');
720
- }
721
- });
722
-
723
- it('should serializes negative memory count without error (no validation)', () => {
724
- // The output module does NOT validate - it just serializes
725
- const category: OutputCategory = {
726
- path: 'test/negative-count',
727
- memories: [],
728
- subcategories: [{ path: 'test/invalid-sub', memoryCount: -1 }],
729
- };
730
- const result = toonSerialize('category', category);
731
-
732
- // Serialization succeeds - validation is at object construction time
733
- expect(result.ok()).toBe(true);
734
- if (result.ok()) {
735
- expect(result.value).toContain('-1');
736
- }
737
- });
738
-
739
- it('should serializes whitespace memory path in category without error (no validation)', () => {
740
- // The output module does NOT validate - it just serializes
741
- const category: OutputCategory = {
742
- path: 'test/valid-path',
743
- memories: [{ path: ' ', tokenEstimate: 10 }], // Whitespace path - serializes as-is
744
- subcategories: [],
745
- };
746
- const result = toonSerialize('category', category);
747
-
748
- // Serialization succeeds - validation is at object construction time
749
- expect(result.ok()).toBe(true);
750
- });
751
-
752
- it('should serializes empty subcategory path without error (no validation)', () => {
753
- // The output module does NOT validate - it just serializes
754
- const category: OutputCategory = {
755
- path: 'test/valid-path',
756
- memories: [],
757
- subcategories: [{ path: '', memoryCount: 5 }], // Empty path - serializes as-is
758
- };
759
- const result = toonSerialize('category', category);
760
-
761
- // Serialization succeeds - validation is at object construction time
762
- expect(result.ok()).toBe(true);
763
- });
764
-
765
- it('should serializes negative token estimate in category memory without error (no validation)', () => {
766
- // The output module does NOT validate - it just serializes
767
- const category: OutputCategory = {
768
- path: 'test/valid-path',
769
- memories: [{ path: 'test/mem', tokenEstimate: -10 }],
770
- subcategories: [],
771
- };
772
- const result = toonSerialize('category', category);
773
-
774
- // Serialization succeeds - validation is at object construction time
775
- expect(result.ok()).toBe(true);
776
- if (result.ok()) {
777
- expect(result.value).toContain('-10');
778
- }
779
- });
780
- });
781
-
782
- describe('TOON format output structure', () => {
783
- it('should output uses newlines between top-level key-value pairs', () => {
784
- const store: OutputStore = {
785
- name: 'test-store',
786
- path: '/test/path',
787
- };
788
- const result = toonSerialize('store', store);
789
-
790
- expect(result.ok()).toBe(true);
791
- if (result.ok()) {
792
- // Newline between fields for YAML-like output
793
- expect(result.value).toContain('\n');
794
- expect(result.value).toContain('name: test-store');
795
- expect(result.value).toContain('path: /test/path');
796
- }
797
- });
798
-
799
- it('should output uses nested structure for metadata (not key folding)', () => {
800
- const memory: OutputMemory = {
801
- path: 'test/folding',
802
- metadata: {
803
- createdAt: new Date('2024-01-15T10:00:00Z'),
804
- tags: ['test'],
805
- },
806
- content: 'Test folding',
807
- };
808
- const result = toonSerialize('memory', memory);
809
-
810
- expect(result.ok()).toBe(true);
811
- if (result.ok()) {
812
- // TOON outputs nested YAML-like format, not inline object braces
813
- expect(result.value).not.toMatch(/metadata:\{/);
814
- // Should have nested indented structure
815
- expect(result.value).toContain('metadata:');
816
- }
817
- });
818
-
819
- it('should tabular format uses correct header syntax with tab separators', () => {
820
- const registry: OutputStoreRegistry = {
821
- stores: [
822
- { name: 'a', path: '/a' },
823
- { name: 'b', path: '/b' },
824
- { name: 'c', path: '/c' },
825
- ],
826
- };
827
- const result = toonSerialize('store-registry', registry);
828
-
829
- expect(result.ok()).toBe(true);
830
- if (result.ok()) {
831
- // Header format with tab-separated count: key[count\t]{fields}:
832
- expect(result.value).toMatch(/stores\[3\t\]\{name\tpath\}:/);
833
- }
834
- });
835
- });