@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,320 @@
1
+ import { Command } from 'commander';
2
+ import inquirer from 'inquirer';
3
+ import chalk from 'chalk';
4
+ import {
5
+ listFacts,
6
+ getFactById,
7
+ createFact,
8
+ updateFact,
9
+ deleteFact,
10
+ searchFacts,
11
+ } from '@memnexus-ai/mx-typescript-sdk';
12
+ import { getApiOptions } from '../lib/api-client';
13
+ import { handleError } from '../lib/errors';
14
+ import { printOutput } from '../lib/formatters';
15
+ import { OutputFormat } from '../types';
16
+
17
+ /**
18
+ * Register semantic facts management commands
19
+ * @param program - Commander program instance
20
+ */
21
+ export function registerFactsCommands(program: Command): void {
22
+ const facts = program.command('facts').description('Manage semantic facts');
23
+
24
+ /**
25
+ * mx facts list [options]
26
+ * List facts with pagination
27
+ */
28
+ facts
29
+ .command('list')
30
+ .description('List facts')
31
+ .option('--page <number>', 'Page number', '0')
32
+ .option('--limit <number>', 'Results per page', '20')
33
+ .option('--format <format>', 'Output format (json|table|yaml)')
34
+ .action(async (options) => {
35
+ try {
36
+ const page = parseInt(options.page, 10);
37
+ const limit = parseInt(options.limit, 10);
38
+ const offset = page * limit;
39
+
40
+ const result = await listFacts({
41
+ ...getApiOptions(),
42
+ query: { limit, offset },
43
+ });
44
+
45
+ // Format for table display
46
+ if (options.format === 'table' || !options.format) {
47
+ const tableData = (result.data?.data || []).map((f: any) => ({
48
+ id: f.id,
49
+ subject: f.subject?.substring(0, 20) + (f.subject?.length > 20 ? '...' : ''),
50
+ predicate: f.predicate,
51
+ object: f.object?.substring(0, 20) + (f.object?.length > 20 ? '...' : ''),
52
+ confidence: f.confidence?.toFixed(2) || 'N/A',
53
+ }));
54
+
55
+ printOutput(tableData, options.format as OutputFormat, {
56
+ columns: ['id', 'subject', 'predicate', 'object', 'confidence'],
57
+ maxColumnWidth: 25,
58
+ });
59
+
60
+ console.log(
61
+ chalk.gray(
62
+ `\nShowing ${offset + 1}-${offset + (result.data?.data?.length || 0)} of ${
63
+ result.data?.count || 0
64
+ } (Page ${page + 1})`
65
+ )
66
+ );
67
+ } else {
68
+ printOutput(result.data, options.format as OutputFormat);
69
+ }
70
+ } catch (error) {
71
+ handleError(error);
72
+ }
73
+ });
74
+
75
+ /**
76
+ * mx facts get <id>
77
+ * Get a specific fact by ID
78
+ */
79
+ facts
80
+ .command('get <id>')
81
+ .description('Get a specific fact')
82
+ .option('--format <format>', 'Output format (json|table|yaml)')
83
+ .action(async (id: string, options) => {
84
+ try {
85
+ const result = await getFactById({
86
+ ...getApiOptions(),
87
+ path: { id },
88
+ });
89
+ printOutput(result.data?.data, options.format as OutputFormat);
90
+ } catch (error) {
91
+ handleError(error);
92
+ }
93
+ });
94
+
95
+ /**
96
+ * mx facts create [options]
97
+ * Create a new fact
98
+ */
99
+ facts
100
+ .command('create')
101
+ .description('Create a new fact')
102
+ .option('--subject <text>', 'Fact subject (entity name)')
103
+ .option('--predicate <text>', 'Relationship type')
104
+ .option('--object <text>', 'Fact object (related entity)')
105
+ .option('--confidence <number>', 'Confidence score (0-1)', '1.0')
106
+ .option('--memory-id <id>', 'Associated memory ID')
107
+ .option('--interactive', 'Interactive mode')
108
+ .option('--format <format>', 'Output format (json|table|yaml)')
109
+ .action(async (options) => {
110
+ try {
111
+ let factData: any = {};
112
+
113
+ if (options.interactive || !options.subject || !options.predicate || !options.object) {
114
+ // Interactive mode
115
+ const answers = await inquirer.prompt([
116
+ {
117
+ type: 'input',
118
+ name: 'subject',
119
+ message: 'Subject (entity name):',
120
+ default: options.subject,
121
+ validate: (input: string) => input.trim().length > 0 || 'Subject is required',
122
+ },
123
+ {
124
+ type: 'input',
125
+ name: 'predicate',
126
+ message: 'Predicate (relationship):',
127
+ default: options.predicate,
128
+ validate: (input: string) => input.trim().length > 0 || 'Predicate is required',
129
+ },
130
+ {
131
+ type: 'input',
132
+ name: 'object',
133
+ message: 'Object (related entity):',
134
+ default: options.object,
135
+ validate: (input: string) => input.trim().length > 0 || 'Object is required',
136
+ },
137
+ {
138
+ type: 'number',
139
+ name: 'confidence',
140
+ message: 'Confidence (0-1):',
141
+ default: parseFloat(options.confidence) || 1.0,
142
+ validate: (input: number) => {
143
+ if (input < 0 || input > 1) return 'Confidence must be between 0 and 1';
144
+ return true;
145
+ },
146
+ },
147
+ {
148
+ type: 'input',
149
+ name: 'memoryId',
150
+ message: 'Memory ID (optional):',
151
+ default: options.memoryId,
152
+ },
153
+ ]);
154
+
155
+ factData = {
156
+ subject: answers.subject,
157
+ predicate: answers.predicate,
158
+ object: answers.object,
159
+ confidence: answers.confidence,
160
+ memoryId: answers.memoryId || undefined,
161
+ };
162
+ } else {
163
+ // Direct mode
164
+ factData = {
165
+ subject: options.subject,
166
+ predicate: options.predicate,
167
+ object: options.object,
168
+ confidence: parseFloat(options.confidence),
169
+ memoryId: options.memoryId,
170
+ };
171
+ }
172
+
173
+ const result = await createFact({
174
+ ...getApiOptions(),
175
+ body: factData,
176
+ });
177
+
178
+ const fact = result.data?.data;
179
+ console.log(chalk.green(`✓ Fact created with ID: ${fact?.id}`));
180
+ printOutput(fact, options.format as OutputFormat);
181
+ } catch (error) {
182
+ handleError(error);
183
+ }
184
+ });
185
+
186
+ /**
187
+ * mx facts update <id> [options]
188
+ * Update an existing fact
189
+ */
190
+ facts
191
+ .command('update <id>')
192
+ .description('Update an existing fact')
193
+ .option('--subject <text>', 'Updated subject')
194
+ .option('--predicate <text>', 'Updated predicate')
195
+ .option('--object <text>', 'Updated object')
196
+ .option('--confidence <number>', 'Updated confidence (0-1)')
197
+ .option('--format <format>', 'Output format (json|table|yaml)')
198
+ .action(async (id: string, options) => {
199
+ try {
200
+ const updates: any = {};
201
+
202
+ if (options.subject) updates.subject = options.subject;
203
+ if (options.predicate) updates.predicate = options.predicate;
204
+ if (options.object) updates.object = options.object;
205
+ if (options.confidence) {
206
+ const conf = parseFloat(options.confidence);
207
+ if (conf < 0 || conf > 1) {
208
+ console.log(chalk.red('Error: Confidence must be between 0 and 1'));
209
+ return;
210
+ }
211
+ updates.confidence = conf;
212
+ }
213
+
214
+ if (Object.keys(updates).length === 0) {
215
+ console.log(chalk.yellow('No updates provided. Use --help to see available options.'));
216
+ return;
217
+ }
218
+
219
+ const result = await updateFact({
220
+ ...getApiOptions(),
221
+ path: { id },
222
+ body: updates,
223
+ });
224
+
225
+ const fact = result.data?.data;
226
+ console.log(chalk.green(`✓ Fact ${id} updated successfully`));
227
+ printOutput(fact, options.format as OutputFormat);
228
+ } catch (error) {
229
+ handleError(error);
230
+ }
231
+ });
232
+
233
+ /**
234
+ * mx facts delete <id>
235
+ * Delete a fact
236
+ */
237
+ facts
238
+ .command('delete <id>')
239
+ .description('Delete a fact')
240
+ .option('--force', 'Skip confirmation prompt')
241
+ .action(async (id: string, options) => {
242
+ try {
243
+ if (!options.force) {
244
+ const answers = await inquirer.prompt([
245
+ {
246
+ type: 'confirm',
247
+ name: 'confirm',
248
+ message: `Are you sure you want to delete fact ${id}?`,
249
+ default: false,
250
+ },
251
+ ]);
252
+
253
+ if (!answers.confirm) {
254
+ console.log(chalk.yellow('Delete cancelled'));
255
+ return;
256
+ }
257
+ }
258
+
259
+ await deleteFact({
260
+ ...getApiOptions(),
261
+ path: { id },
262
+ });
263
+
264
+ console.log(chalk.green(`✓ Fact ${id} deleted successfully`));
265
+ } catch (error) {
266
+ handleError(error);
267
+ }
268
+ });
269
+
270
+ /**
271
+ * mx facts search [options]
272
+ * Search facts
273
+ */
274
+ facts
275
+ .command('search')
276
+ .description('Search facts')
277
+ .option('--query <text>', 'Search query')
278
+ .option('--limit <number>', 'Maximum results', '20')
279
+ .option('--format <format>', 'Output format (json|table|yaml)')
280
+ .action(async (options) => {
281
+ try {
282
+ if (!options.query) {
283
+ console.log(chalk.red('Error: --query is required'));
284
+ return;
285
+ }
286
+
287
+ const result = await searchFacts({
288
+ ...getApiOptions(),
289
+ body: {
290
+ query: options.query,
291
+ limit: parseInt(options.limit, 10),
292
+ },
293
+ });
294
+
295
+ const searchData = result.data;
296
+
297
+ // Format for table display
298
+ if (options.format === 'table' || !options.format) {
299
+ const tableData = (searchData?.data || []).map((f: any) => ({
300
+ id: f.id || 'N/A',
301
+ subject: f.subject?.substring(0, 20) + (f.subject?.length > 20 ? '...' : ''),
302
+ predicate: f.predicate,
303
+ object: f.object?.substring(0, 20) + (f.object?.length > 20 ? '...' : ''),
304
+ confidence: f.confidence?.toFixed(2) || 'N/A',
305
+ }));
306
+
307
+ printOutput(tableData, options.format as OutputFormat, {
308
+ columns: ['id', 'subject', 'predicate', 'object', 'confidence'],
309
+ maxColumnWidth: 25,
310
+ });
311
+
312
+ console.log(chalk.gray(`\nFound ${searchData?.count || 0} results`));
313
+ } else {
314
+ printOutput(searchData, options.format as OutputFormat);
315
+ }
316
+ } catch (error) {
317
+ handleError(error);
318
+ }
319
+ });
320
+ }
@@ -0,0 +1,149 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import {
5
+ executeGraphRagQuery,
6
+ explainGraphRagQuery,
7
+ queryCommunities,
8
+ } from '@memnexus-ai/mx-typescript-sdk';
9
+ import { getApiOptions } from '../lib/api-client';
10
+ import { handleError } from '../lib/errors';
11
+ import { printOutput } from '../lib/formatters';
12
+ import { OutputFormat } from '../types';
13
+
14
+ /**
15
+ * Register GraphRAG query commands
16
+ * @param program - Commander program instance
17
+ */
18
+ export function registerGraphragCommands(program: Command): void {
19
+ const graphrag = program.command('graphrag').description('Execute GraphRAG queries');
20
+
21
+ /**
22
+ * mx graphrag query [options]
23
+ * Execute a GraphRAG query
24
+ */
25
+ graphrag
26
+ .command('query')
27
+ .description('Execute GraphRAG query')
28
+ .option('--query <text>', 'Query text (required)')
29
+ .option('--context-id <id>', 'Context ID')
30
+ .option('--mode <mode>', 'Query mode (local|global|hybrid)', 'hybrid')
31
+ .option('--max-depth <number>', 'Graph traversal depth', '3')
32
+ .option('--limit <number>', 'Max results', '20')
33
+ .option('--format <format>', 'Output format (json|table|yaml)')
34
+ .action(async (options) => {
35
+ try {
36
+ if (!options.query) {
37
+ console.log(chalk.red('Error: --query is required'));
38
+ return;
39
+ }
40
+
41
+ const spinner = ora('Executing GraphRAG query...').start();
42
+
43
+ const result = await executeGraphRagQuery({
44
+ ...getApiOptions(),
45
+ body: {
46
+ query: options.query,
47
+ maxDepth: parseInt(options.maxDepth, 10),
48
+ limit: parseInt(options.limit, 10),
49
+ },
50
+ });
51
+
52
+ spinner.succeed('Query executed successfully');
53
+
54
+ const responseData = result.data as any;
55
+ if (options.format === 'table' || !options.format) {
56
+ const tableData = (responseData?.data || []).map((r: any) => ({
57
+ id: r.id,
58
+ answer: r.answer?.substring(0, 50) + (r.answer?.length > 50 ? '...' : ''),
59
+ sources: r.sources?.length || 0,
60
+ confidence: r.confidence || 0,
61
+ }));
62
+
63
+ printOutput(tableData, options.format as OutputFormat, {
64
+ columns: ['id', 'answer', 'sources', 'confidence'],
65
+ maxColumnWidth: 25,
66
+ });
67
+ } else {
68
+ printOutput(responseData, options.format as OutputFormat);
69
+ }
70
+ } catch (error) {
71
+ handleError(error);
72
+ }
73
+ });
74
+
75
+ /**
76
+ * mx graphrag explain <result-id>
77
+ * Explain a query result
78
+ */
79
+ graphrag
80
+ .command('explain <result-id>')
81
+ .description('Explain query result')
82
+ .option('--format <format>', 'Output format (json|table|yaml)')
83
+ .action(async (resultId: string, options) => {
84
+ try {
85
+ const spinner = ora('Generating explanation...').start();
86
+
87
+ const result = await explainGraphRagQuery({
88
+ ...getApiOptions(),
89
+ query: { queryId: resultId },
90
+ });
91
+
92
+ spinner.succeed('Explanation generated');
93
+ printOutput(result.data, options.format as OutputFormat);
94
+ } catch (error) {
95
+ handleError(error);
96
+ }
97
+ });
98
+
99
+ /**
100
+ * mx graphrag query-communities [options]
101
+ * Query at community level
102
+ */
103
+ graphrag
104
+ .command('query-communities')
105
+ .description('Query communities')
106
+ .option('--query <text>', 'Query text (required)')
107
+ .option('--context-id <id>', 'Context ID')
108
+ .option('--limit <number>', 'Max communities', '10')
109
+ .option('--format <format>', 'Output format (json|table|yaml)')
110
+ .action(async (options) => {
111
+ try {
112
+ if (!options.query) {
113
+ console.log(chalk.red('Error: --query is required'));
114
+ return;
115
+ }
116
+
117
+ const spinner = ora('Querying communities...').start();
118
+
119
+ const result = await queryCommunities({
120
+ ...getApiOptions(),
121
+ body: {
122
+ query: options.query,
123
+ limit: parseInt(options.limit, 10),
124
+ },
125
+ });
126
+
127
+ spinner.succeed('Community query completed');
128
+
129
+ const responseData = result.data as any;
130
+ if (options.format === 'table' || !options.format) {
131
+ const tableData = (responseData?.data || []).map((c: any) => ({
132
+ communityId: c.communityId,
133
+ communityName: c.communityName || 'N/A',
134
+ answer: c.answer?.substring(0, 40) + (c.answer?.length > 40 ? '...' : ''),
135
+ confidence: c.confidence || 0,
136
+ }));
137
+
138
+ printOutput(tableData, options.format as OutputFormat, {
139
+ columns: ['communityId', 'communityName', 'answer', 'confidence'],
140
+ maxColumnWidth: 20,
141
+ });
142
+ } else {
143
+ printOutput(responseData, options.format as OutputFormat);
144
+ }
145
+ } catch (error) {
146
+ handleError(error);
147
+ }
148
+ });
149
+ }