@memnexus-ai/cli 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.
Files changed (148) hide show
  1. package/.env.example +13 -0
  2. package/.eslintrc.js +24 -0
  3. package/.github/ISSUE_TEMPLATE/phase-1-foundation.md +1078 -0
  4. package/.github/workflows/publish.yml +277 -0
  5. package/.github/workflows/test-app-token.yml +54 -0
  6. package/.npmrc.backup +3 -0
  7. package/.npmrc.example +6 -0
  8. package/.prettierignore +4 -0
  9. package/.prettierrc +8 -0
  10. package/CHANGELOG.md +138 -0
  11. package/PLATFORM_TESTING.md +243 -0
  12. package/README.md +986 -0
  13. package/RELEASE.md +428 -0
  14. package/RELEASE_READINESS.md +253 -0
  15. package/USAGE.md +1373 -0
  16. package/bin/mx.js +2 -0
  17. package/dist/commands/apikeys.d.ts +7 -0
  18. package/dist/commands/apikeys.d.ts.map +1 -0
  19. package/dist/commands/apikeys.js +133 -0
  20. package/dist/commands/apikeys.js.map +1 -0
  21. package/dist/commands/artifacts.d.ts +7 -0
  22. package/dist/commands/artifacts.d.ts.map +1 -0
  23. package/dist/commands/artifacts.js +277 -0
  24. package/dist/commands/artifacts.js.map +1 -0
  25. package/dist/commands/auth.d.ts +7 -0
  26. package/dist/commands/auth.d.ts.map +1 -0
  27. package/dist/commands/auth.js +119 -0
  28. package/dist/commands/auth.js.map +1 -0
  29. package/dist/commands/communities.d.ts +7 -0
  30. package/dist/commands/communities.d.ts.map +1 -0
  31. package/dist/commands/communities.js +137 -0
  32. package/dist/commands/communities.js.map +1 -0
  33. package/dist/commands/config.d.ts +7 -0
  34. package/dist/commands/config.d.ts.map +1 -0
  35. package/dist/commands/config.js +138 -0
  36. package/dist/commands/config.js.map +1 -0
  37. package/dist/commands/conversations.d.ts +7 -0
  38. package/dist/commands/conversations.d.ts.map +1 -0
  39. package/dist/commands/conversations.js +160 -0
  40. package/dist/commands/conversations.js.map +1 -0
  41. package/dist/commands/facts.d.ts +7 -0
  42. package/dist/commands/facts.d.ts.map +1 -0
  43. package/dist/commands/facts.js +298 -0
  44. package/dist/commands/facts.js.map +1 -0
  45. package/dist/commands/graphrag.d.ts +7 -0
  46. package/dist/commands/graphrag.d.ts.map +1 -0
  47. package/dist/commands/graphrag.js +139 -0
  48. package/dist/commands/graphrag.js.map +1 -0
  49. package/dist/commands/memories.d.ts +7 -0
  50. package/dist/commands/memories.d.ts.map +1 -0
  51. package/dist/commands/memories.js +304 -0
  52. package/dist/commands/memories.js.map +1 -0
  53. package/dist/commands/patterns.d.ts +7 -0
  54. package/dist/commands/patterns.d.ts.map +1 -0
  55. package/dist/commands/patterns.js +227 -0
  56. package/dist/commands/patterns.js.map +1 -0
  57. package/dist/commands/system.d.ts +7 -0
  58. package/dist/commands/system.d.ts.map +1 -0
  59. package/dist/commands/system.js +97 -0
  60. package/dist/commands/system.js.map +1 -0
  61. package/dist/commands/topics.d.ts +7 -0
  62. package/dist/commands/topics.d.ts.map +1 -0
  63. package/dist/commands/topics.js +314 -0
  64. package/dist/commands/topics.js.map +1 -0
  65. package/dist/index.d.ts +3 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +44 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/lib/api-client.d.ts +29 -0
  70. package/dist/lib/api-client.d.ts.map +1 -0
  71. package/dist/lib/api-client.js +64 -0
  72. package/dist/lib/api-client.js.map +1 -0
  73. package/dist/lib/auth.d.ts +10 -0
  74. package/dist/lib/auth.d.ts.map +1 -0
  75. package/dist/lib/auth.js +47 -0
  76. package/dist/lib/auth.js.map +1 -0
  77. package/dist/lib/config.d.ts +19 -0
  78. package/dist/lib/config.d.ts.map +1 -0
  79. package/dist/lib/config.js +59 -0
  80. package/dist/lib/config.js.map +1 -0
  81. package/dist/lib/errors.d.ts +7 -0
  82. package/dist/lib/errors.d.ts.map +1 -0
  83. package/dist/lib/errors.js +133 -0
  84. package/dist/lib/errors.js.map +1 -0
  85. package/dist/lib/formatters.d.ts +12 -0
  86. package/dist/lib/formatters.d.ts.map +1 -0
  87. package/dist/lib/formatters.js +103 -0
  88. package/dist/lib/formatters.js.map +1 -0
  89. package/dist/lib/spinner.d.ts +54 -0
  90. package/dist/lib/spinner.d.ts.map +1 -0
  91. package/dist/lib/spinner.js +108 -0
  92. package/dist/lib/spinner.js.map +1 -0
  93. package/dist/lib/validators.d.ts +92 -0
  94. package/dist/lib/validators.d.ts.map +1 -0
  95. package/dist/lib/validators.js +257 -0
  96. package/dist/lib/validators.js.map +1 -0
  97. package/dist/types/index.d.ts +13 -0
  98. package/dist/types/index.d.ts.map +1 -0
  99. package/dist/types/index.js +3 -0
  100. package/dist/types/index.js.map +1 -0
  101. package/docs/README.md +219 -0
  102. package/docs/code-generation-strategy.md +560 -0
  103. package/docs/prd.md +748 -0
  104. package/docs/sync-strategy.md +533 -0
  105. package/jest.config.js +30 -0
  106. package/package.json +67 -0
  107. package/scripts/install-deps.sh +38 -0
  108. package/src/commands/apikeys.ts +144 -0
  109. package/src/commands/artifacts.ts +296 -0
  110. package/src/commands/auth.ts +122 -0
  111. package/src/commands/communities.ts +153 -0
  112. package/src/commands/config.ts +144 -0
  113. package/src/commands/conversations.ts +176 -0
  114. package/src/commands/facts.ts +320 -0
  115. package/src/commands/graphrag.ts +149 -0
  116. package/src/commands/memories.ts +332 -0
  117. package/src/commands/patterns.ts +251 -0
  118. package/src/commands/system.ts +102 -0
  119. package/src/commands/topics.ts +354 -0
  120. package/src/index.ts +43 -0
  121. package/src/lib/api-client.ts +68 -0
  122. package/src/lib/auth.ts +42 -0
  123. package/src/lib/config.ts +68 -0
  124. package/src/lib/errors.ts +143 -0
  125. package/src/lib/formatters.ts +123 -0
  126. package/src/lib/spinner.ts +113 -0
  127. package/src/lib/validators.ts +302 -0
  128. package/src/types/index.ts +17 -0
  129. package/tests/__mocks__/chalk.ts +16 -0
  130. package/tests/__mocks__/cli-table3.ts +37 -0
  131. package/tests/__mocks__/configstore.ts +38 -0
  132. package/tests/commands/apikeys.test.ts +179 -0
  133. package/tests/commands/artifacts.test.ts +194 -0
  134. package/tests/commands/auth.test.ts +120 -0
  135. package/tests/commands/communities.test.ts +154 -0
  136. package/tests/commands/config.test.ts +154 -0
  137. package/tests/commands/conversations.test.ts +136 -0
  138. package/tests/commands/facts.test.ts +210 -0
  139. package/tests/commands/graphrag.test.ts +194 -0
  140. package/tests/commands/memories.test.ts +215 -0
  141. package/tests/commands/patterns.test.ts +201 -0
  142. package/tests/commands/system.test.ts +172 -0
  143. package/tests/commands/topics.test.ts +274 -0
  144. package/tests/lib/auth.test.ts +77 -0
  145. package/tests/lib/config.test.ts +50 -0
  146. package/tests/lib/errors.test.ts +126 -0
  147. package/tests/lib/formatters.test.ts +87 -0
  148. package/tsconfig.json +20 -0
@@ -0,0 +1,154 @@
1
+ import { getApiClient } from '../../src/lib/api-client';
2
+
3
+ // Mock the API client
4
+ jest.mock('../../src/lib/api-client');
5
+
6
+ // Mock chalk
7
+ jest.mock('chalk', () => ({
8
+ default: {
9
+ green: (str: string) => str,
10
+ red: (str: string) => str,
11
+ yellow: (str: string) => str,
12
+ cyan: (str: string) => str,
13
+ gray: (str: string) => str,
14
+ },
15
+ green: (str: string) => str,
16
+ red: (str: string) => str,
17
+ yellow: (str: string) => str,
18
+ cyan: (str: string) => str,
19
+ gray: (str: string) => str,
20
+ }));
21
+
22
+ // Mock ora
23
+ jest.mock('ora', () => {
24
+ return jest.fn(() => ({
25
+ start: jest.fn(function() { return this; }),
26
+ succeed: jest.fn(function() { return this; }),
27
+ fail: jest.fn(function() { return this; }),
28
+ }));
29
+ });
30
+
31
+ describe('Communities Commands', () => {
32
+ let mockClient: any;
33
+
34
+ beforeEach(() => {
35
+ jest.clearAllMocks();
36
+
37
+ mockClient = {
38
+ communities: {
39
+ list: jest.fn(),
40
+ get: jest.fn(),
41
+ merge: jest.fn(),
42
+ },
43
+ };
44
+
45
+ (getApiClient as jest.Mock).mockReturnValue(mockClient);
46
+ });
47
+
48
+ describe('communities list', () => {
49
+ it('should list communities with default pagination', async () => {
50
+ const mockResponse = {
51
+ data: [
52
+ {
53
+ id: 'comm_1',
54
+ name: 'Community 1',
55
+ memberCount: 5,
56
+ topicCount: 3,
57
+ createdAt: '2024-01-01T00:00:00Z',
58
+ },
59
+ {
60
+ id: 'comm_2',
61
+ name: 'Community 2',
62
+ memberCount: 8,
63
+ topicCount: 5,
64
+ createdAt: '2024-01-02T00:00:00Z',
65
+ },
66
+ ],
67
+ pagination: {
68
+ limit: 20,
69
+ offset: 0,
70
+ count: 2,
71
+ },
72
+ };
73
+
74
+ mockClient.communities.list.mockResolvedValue(mockResponse);
75
+
76
+ const result = await mockClient.communities.list({ limit: 20, offset: 0 });
77
+
78
+ expect(mockClient.communities.list).toHaveBeenCalledWith({ limit: 20, offset: 0 });
79
+ expect(result.data).toHaveLength(2);
80
+ expect(result.pagination.count).toBe(2);
81
+ });
82
+
83
+ it('should handle context and min-size filters', async () => {
84
+ const mockResponse = {
85
+ data: [],
86
+ pagination: { limit: 20, offset: 0, count: 0 },
87
+ };
88
+
89
+ mockClient.communities.list.mockResolvedValue(mockResponse);
90
+
91
+ await mockClient.communities.list({
92
+ limit: 20,
93
+ offset: 0,
94
+ contextId: 'ctx_123',
95
+ minSize: 3,
96
+ });
97
+
98
+ expect(mockClient.communities.list).toHaveBeenCalledWith({
99
+ limit: 20,
100
+ offset: 0,
101
+ contextId: 'ctx_123',
102
+ minSize: 3,
103
+ });
104
+ });
105
+ });
106
+
107
+ describe('communities get', () => {
108
+ it('should get community details', async () => {
109
+ const mockCommunity = {
110
+ id: 'comm_1',
111
+ name: 'Community 1',
112
+ memberCount: 5,
113
+ topicCount: 3,
114
+ createdAt: '2024-01-01T00:00:00Z',
115
+ members: ['topic_1', 'topic_2', 'topic_3'],
116
+ };
117
+
118
+ mockClient.communities.get.mockResolvedValue(mockCommunity);
119
+
120
+ const result = await mockClient.communities.get('comm_1');
121
+
122
+ expect(mockClient.communities.get).toHaveBeenCalledWith('comm_1');
123
+ expect(result.id).toBe('comm_1');
124
+ expect(result.name).toBe('Community 1');
125
+ expect(result.members).toHaveLength(3);
126
+ });
127
+ });
128
+
129
+ describe('communities merge', () => {
130
+ it('should merge two communities', async () => {
131
+ const mockResult = {
132
+ id: 'comm_2',
133
+ name: 'Community 2',
134
+ memberCount: 13,
135
+ topicCount: 8,
136
+ };
137
+
138
+ mockClient.communities.merge.mockResolvedValue(mockResult);
139
+
140
+ const result = await mockClient.communities.merge({
141
+ sourceId: 'comm_1',
142
+ targetId: 'comm_2',
143
+ });
144
+
145
+ expect(mockClient.communities.merge).toHaveBeenCalledWith({
146
+ sourceId: 'comm_1',
147
+ targetId: 'comm_2',
148
+ });
149
+ expect(result.memberCount).toBe(13);
150
+ expect(result.topicCount).toBe(8);
151
+ });
152
+ });
153
+ });
154
+
@@ -0,0 +1,154 @@
1
+ import { config } from '../../src/lib/config';
2
+
3
+ // Mock configstore
4
+ jest.mock('configstore');
5
+
6
+ // Mock chalk
7
+ jest.mock('chalk', () => ({
8
+ default: {
9
+ green: (str: string) => str,
10
+ red: (str: string) => str,
11
+ yellow: (str: string) => str,
12
+ cyan: (str: string) => str,
13
+ gray: (str: string) => str,
14
+ },
15
+ green: (str: string) => str,
16
+ red: (str: string) => str,
17
+ yellow: (str: string) => str,
18
+ cyan: (str: string) => str,
19
+ gray: (str: string) => str,
20
+ }));
21
+
22
+ describe('Config Commands', () => {
23
+ beforeEach(() => {
24
+ jest.clearAllMocks();
25
+ // Clear environment variables
26
+ delete process.env.MX_API_URL;
27
+ delete process.env.MX_OUTPUT_FORMAT;
28
+ });
29
+
30
+ describe('config.get', () => {
31
+ it('should get a specific config value', () => {
32
+ const mockGet = jest.spyOn(config, 'get');
33
+ mockGet.mockReturnValue('https://api.memnexus.io');
34
+
35
+ const value = config.get('apiUrl');
36
+
37
+ expect(value).toBe('https://api.memnexus.io');
38
+ expect(mockGet).toHaveBeenCalledWith('apiUrl');
39
+
40
+ mockGet.mockRestore();
41
+ });
42
+
43
+ it('should return all config when no key provided', () => {
44
+ const mockGet = jest.spyOn(config, 'get');
45
+ const mockConfig = {
46
+ apiUrl: 'https://api.memnexus.io',
47
+ defaultFormat: 'table',
48
+ defaultPageSize: 20,
49
+ };
50
+ mockGet.mockReturnValue(mockConfig);
51
+
52
+ const value = config.get();
53
+
54
+ expect(value).toEqual(mockConfig);
55
+
56
+ mockGet.mockRestore();
57
+ });
58
+ });
59
+
60
+ describe('config.set', () => {
61
+ it('should set a valid config value', () => {
62
+ const mockSet = jest.spyOn(config, 'set');
63
+
64
+ config.set('apiUrl', 'https://custom.api.com');
65
+
66
+ expect(mockSet).toHaveBeenCalledWith('apiUrl', 'https://custom.api.com');
67
+
68
+ mockSet.mockRestore();
69
+ });
70
+
71
+ it('should set defaultPageSize as number', () => {
72
+ const mockSet = jest.spyOn(config, 'set');
73
+
74
+ config.set('defaultPageSize', 50);
75
+
76
+ expect(mockSet).toHaveBeenCalledWith('defaultPageSize', 50);
77
+
78
+ mockSet.mockRestore();
79
+ });
80
+ });
81
+
82
+ describe('config.reset', () => {
83
+ it('should reset config to defaults', () => {
84
+ const mockReset = jest.spyOn(config, 'reset');
85
+
86
+ config.reset();
87
+
88
+ expect(mockReset).toHaveBeenCalled();
89
+
90
+ mockReset.mockRestore();
91
+ });
92
+ });
93
+
94
+ describe('config.getApiUrl', () => {
95
+ it('should return environment variable if set', () => {
96
+ process.env.MX_API_URL = 'https://env.api.com';
97
+
98
+ const url = config.getApiUrl();
99
+
100
+ expect(url).toBe('https://env.api.com');
101
+ });
102
+
103
+ it('should return default value if no environment variable or config', () => {
104
+ delete process.env.MX_API_URL;
105
+
106
+ const url = config.getApiUrl();
107
+
108
+ // Should return the default API URL
109
+ expect(url).toBe('https://api.memnexus.io');
110
+ });
111
+ });
112
+
113
+ describe('config.getFormat', () => {
114
+ it('should return environment variable if set', () => {
115
+ process.env.MX_OUTPUT_FORMAT = 'json';
116
+
117
+ const format = config.getFormat();
118
+
119
+ expect(format).toBe('json');
120
+ });
121
+
122
+ it('should return default format if no override', () => {
123
+ const format = config.getFormat();
124
+
125
+ expect(['json', 'table', 'yaml']).toContain(format);
126
+ });
127
+ });
128
+
129
+ describe('config validation', () => {
130
+ it('should validate URL format', () => {
131
+ const validUrl = 'https://api.example.com';
132
+ expect(() => new URL(validUrl)).not.toThrow();
133
+ });
134
+
135
+ it('should reject invalid URL format', () => {
136
+ const invalidUrl = 'not-a-url';
137
+ expect(() => new URL(invalidUrl)).toThrow();
138
+ });
139
+
140
+ it('should validate format values', () => {
141
+ const validFormats = ['json', 'table', 'yaml'];
142
+ validFormats.forEach((format) => {
143
+ expect(validFormats).toContain(format);
144
+ });
145
+ });
146
+
147
+ it('should validate page size range', () => {
148
+ const validPageSize = 50;
149
+ expect(validPageSize).toBeGreaterThanOrEqual(1);
150
+ expect(validPageSize).toBeLessThanOrEqual(100);
151
+ });
152
+ });
153
+ });
154
+
@@ -0,0 +1,136 @@
1
+ import { getApiClient } from '../../src/lib/api-client';
2
+
3
+ // Mock the API client
4
+ jest.mock('../../src/lib/api-client');
5
+
6
+ // Mock chalk
7
+ jest.mock('chalk', () => ({
8
+ default: {
9
+ green: (str: string) => str,
10
+ red: (str: string) => str,
11
+ yellow: (str: string) => str,
12
+ cyan: (str: string) => str,
13
+ gray: (str: string) => str,
14
+ },
15
+ green: (str: string) => str,
16
+ red: (str: string) => str,
17
+ yellow: (str: string) => str,
18
+ cyan: (str: string) => str,
19
+ gray: (str: string) => str,
20
+ }));
21
+
22
+ describe('Conversations Commands', () => {
23
+ let mockClient: any;
24
+
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+
28
+ mockClient = {
29
+ conversations: {
30
+ list: jest.fn(),
31
+ create: jest.fn(),
32
+ },
33
+ };
34
+
35
+ (getApiClient as jest.Mock).mockReturnValue(mockClient);
36
+ });
37
+
38
+ describe('conversations list', () => {
39
+ it('should list conversations with default pagination', async () => {
40
+ const mockResponse = {
41
+ data: [
42
+ {
43
+ id: 'conv_1',
44
+ title: 'Test Conversation 1',
45
+ messageCount: 5,
46
+ createdAt: '2024-01-01T00:00:00Z',
47
+ },
48
+ {
49
+ id: 'conv_2',
50
+ title: 'Test Conversation 2',
51
+ messageCount: 3,
52
+ createdAt: '2024-01-02T00:00:00Z',
53
+ },
54
+ ],
55
+ pagination: {
56
+ limit: 20,
57
+ offset: 0,
58
+ count: 2,
59
+ },
60
+ };
61
+
62
+ mockClient.conversations.list.mockResolvedValue(mockResponse);
63
+
64
+ const result = await mockClient.conversations.list({ limit: 20, offset: 0 });
65
+
66
+ expect(mockClient.conversations.list).toHaveBeenCalledWith({ limit: 20, offset: 0 });
67
+ expect(result.data).toHaveLength(2);
68
+ expect(result.pagination.count).toBe(2);
69
+ });
70
+
71
+ it('should handle pagination parameters', async () => {
72
+ const mockResponse = {
73
+ data: [],
74
+ pagination: { limit: 10, offset: 20, count: 0 },
75
+ };
76
+
77
+ mockClient.conversations.list.mockResolvedValue(mockResponse);
78
+
79
+ await mockClient.conversations.list({ limit: 10, offset: 20 });
80
+
81
+ expect(mockClient.conversations.list).toHaveBeenCalledWith({ limit: 10, offset: 20 });
82
+ });
83
+ });
84
+
85
+ describe('conversations create', () => {
86
+ it('should create a conversation', async () => {
87
+ const conversationData = {
88
+ title: 'New Conversation',
89
+ };
90
+
91
+ const mockCreated = {
92
+ id: 'conv_new',
93
+ ...conversationData,
94
+ messageCount: 0,
95
+ createdAt: '2024-01-01T00:00:00Z',
96
+ };
97
+
98
+ mockClient.conversations.create.mockResolvedValue(mockCreated);
99
+
100
+ const result = await mockClient.conversations.create(conversationData);
101
+
102
+ expect(mockClient.conversations.create).toHaveBeenCalledWith(conversationData);
103
+ expect(result.id).toBe('conv_new');
104
+ expect(result.title).toBe('New Conversation');
105
+ });
106
+ });
107
+
108
+ describe('conversations get', () => {
109
+ it('should note that get endpoint is not yet implemented', () => {
110
+ // This is a placeholder test since the get endpoint is not yet in the SDK
111
+ expect(true).toBe(true);
112
+ });
113
+ });
114
+
115
+ describe('conversations timeline', () => {
116
+ it('should note that timeline endpoint is not yet implemented', () => {
117
+ // This is a placeholder test since the timeline endpoint is not yet in the API
118
+ expect(true).toBe(true);
119
+ });
120
+ });
121
+
122
+ describe('conversations search', () => {
123
+ it('should note that search endpoint is not yet implemented', () => {
124
+ // This is a placeholder test since the search endpoint is not yet in the API
125
+ expect(true).toBe(true);
126
+ });
127
+ });
128
+
129
+ describe('conversations by-topic', () => {
130
+ it('should note that by-topic endpoint is not yet implemented', () => {
131
+ // This is a placeholder test since the by-topic endpoint is not yet in the API
132
+ expect(true).toBe(true);
133
+ });
134
+ });
135
+ });
136
+
@@ -0,0 +1,210 @@
1
+ import { getApiClient } from '../../src/lib/api-client';
2
+
3
+ // Mock the API client
4
+ jest.mock('../../src/lib/api-client');
5
+
6
+ // Mock chalk
7
+ jest.mock('chalk', () => ({
8
+ default: {
9
+ green: (str: string) => str,
10
+ red: (str: string) => str,
11
+ yellow: (str: string) => str,
12
+ cyan: (str: string) => str,
13
+ gray: (str: string) => str,
14
+ },
15
+ green: (str: string) => str,
16
+ red: (str: string) => str,
17
+ yellow: (str: string) => str,
18
+ cyan: (str: string) => str,
19
+ gray: (str: string) => str,
20
+ }));
21
+
22
+ describe('Facts Commands', () => {
23
+ let mockClient: any;
24
+
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+
28
+ mockClient = {
29
+ facts: {
30
+ list: jest.fn(),
31
+ get: jest.fn(),
32
+ create: jest.fn(),
33
+ update: jest.fn(),
34
+ delete: jest.fn(),
35
+ search: jest.fn(),
36
+ },
37
+ };
38
+
39
+ (getApiClient as jest.Mock).mockReturnValue(mockClient);
40
+ });
41
+
42
+ describe('facts list', () => {
43
+ it('should list facts with default pagination', async () => {
44
+ const mockResponse = {
45
+ data: [
46
+ {
47
+ id: 'fact_1',
48
+ subject: 'Machine Learning',
49
+ predicate: 'is_a',
50
+ object: 'AI Field',
51
+ confidence: 0.95,
52
+ },
53
+ {
54
+ id: 'fact_2',
55
+ subject: 'Python',
56
+ predicate: 'used_for',
57
+ object: 'Data Science',
58
+ confidence: 0.9,
59
+ },
60
+ ],
61
+ pagination: {
62
+ limit: 20,
63
+ offset: 0,
64
+ count: 2,
65
+ },
66
+ };
67
+
68
+ mockClient.facts.list.mockResolvedValue(mockResponse);
69
+
70
+ const result = await mockClient.facts.list({ limit: 20, offset: 0 });
71
+
72
+ expect(mockClient.facts.list).toHaveBeenCalledWith({ limit: 20, offset: 0 });
73
+ expect(result.data).toHaveLength(2);
74
+ expect(result.pagination.count).toBe(2);
75
+ });
76
+ });
77
+
78
+ describe('facts get', () => {
79
+ it('should get a specific fact by ID', async () => {
80
+ const mockFact = {
81
+ id: 'fact_123',
82
+ subject: 'TypeScript',
83
+ predicate: 'is_a',
84
+ object: 'Programming Language',
85
+ confidence: 1.0,
86
+ };
87
+
88
+ mockClient.facts.get.mockResolvedValue(mockFact);
89
+
90
+ const result = await mockClient.facts.get('fact_123');
91
+
92
+ expect(mockClient.facts.get).toHaveBeenCalledWith('fact_123');
93
+ expect(result.id).toBe('fact_123');
94
+ expect(result.subject).toBe('TypeScript');
95
+ });
96
+ });
97
+
98
+ describe('facts create', () => {
99
+ it('should create a fact with required fields', async () => {
100
+ const factData = {
101
+ subject: 'Node.js',
102
+ predicate: 'is_a',
103
+ object: 'JavaScript Runtime',
104
+ confidence: 1.0,
105
+ };
106
+
107
+ const mockCreated = {
108
+ id: 'fact_new',
109
+ ...factData,
110
+ createdAt: '2024-01-01T00:00:00Z',
111
+ };
112
+
113
+ mockClient.facts.create.mockResolvedValue(mockCreated);
114
+
115
+ const result = await mockClient.facts.create(factData);
116
+
117
+ expect(mockClient.facts.create).toHaveBeenCalledWith(factData);
118
+ expect(result.id).toBe('fact_new');
119
+ expect(result.subject).toBe('Node.js');
120
+ });
121
+
122
+ it('should create a fact with optional memoryId', async () => {
123
+ const factData = {
124
+ subject: 'React',
125
+ predicate: 'is_a',
126
+ object: 'UI Library',
127
+ confidence: 0.95,
128
+ memoryId: 'mem_123',
129
+ };
130
+
131
+ mockClient.facts.create.mockResolvedValue({ id: 'fact_new', ...factData });
132
+
133
+ const result = await mockClient.facts.create(factData);
134
+
135
+ expect(result.memoryId).toBe('mem_123');
136
+ });
137
+
138
+ it('should validate confidence range', () => {
139
+ const validConfidence = 0.75;
140
+ expect(validConfidence).toBeGreaterThanOrEqual(0);
141
+ expect(validConfidence).toBeLessThanOrEqual(1);
142
+ });
143
+ });
144
+
145
+ describe('facts update', () => {
146
+ it('should update fact fields', async () => {
147
+ const updates = {
148
+ confidence: 0.85,
149
+ object: 'Updated Object',
150
+ };
151
+
152
+ const mockUpdated = {
153
+ id: 'fact_123',
154
+ subject: 'Test',
155
+ predicate: 'is_a',
156
+ ...updates,
157
+ };
158
+
159
+ mockClient.facts.update.mockResolvedValue(mockUpdated);
160
+
161
+ const result = await mockClient.facts.update('fact_123', updates);
162
+
163
+ expect(mockClient.facts.update).toHaveBeenCalledWith('fact_123', updates);
164
+ expect(result.confidence).toBe(0.85);
165
+ expect(result.object).toBe('Updated Object');
166
+ });
167
+ });
168
+
169
+ describe('facts delete', () => {
170
+ it('should delete a fact', async () => {
171
+ mockClient.facts.delete.mockResolvedValue(undefined);
172
+
173
+ await mockClient.facts.delete('fact_123');
174
+
175
+ expect(mockClient.facts.delete).toHaveBeenCalledWith('fact_123');
176
+ });
177
+ });
178
+
179
+ describe('facts search', () => {
180
+ it('should search facts with query', async () => {
181
+ const mockResults = {
182
+ data: [
183
+ {
184
+ id: 'fact_1',
185
+ subject: 'Machine Learning',
186
+ predicate: 'is_a',
187
+ object: 'AI',
188
+ confidence: 0.95,
189
+ },
190
+ ],
191
+ count: 1,
192
+ };
193
+
194
+ mockClient.facts.search.mockResolvedValue(mockResults);
195
+
196
+ const result = await mockClient.facts.search({
197
+ query: 'machine learning',
198
+ limit: 10,
199
+ });
200
+
201
+ expect(mockClient.facts.search).toHaveBeenCalledWith({
202
+ query: 'machine learning',
203
+ limit: 10,
204
+ });
205
+ expect(result.data).toHaveLength(1);
206
+ expect(result.count).toBe(1);
207
+ });
208
+ });
209
+ });
210
+