@memnexus-ai/cli 0.1.0 → 0.1.2

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 (60) hide show
  1. package/package.json +9 -2
  2. package/.env.example +0 -13
  3. package/.eslintrc.js +0 -24
  4. package/.github/ISSUE_TEMPLATE/phase-1-foundation.md +0 -1078
  5. package/.github/workflows/publish.yml +0 -277
  6. package/.github/workflows/test-app-token.yml +0 -54
  7. package/.npmrc.backup +0 -3
  8. package/.npmrc.example +0 -6
  9. package/.prettierignore +0 -4
  10. package/.prettierrc +0 -8
  11. package/PLATFORM_TESTING.md +0 -243
  12. package/RELEASE.md +0 -428
  13. package/RELEASE_READINESS.md +0 -253
  14. package/docs/README.md +0 -219
  15. package/docs/code-generation-strategy.md +0 -560
  16. package/docs/prd.md +0 -748
  17. package/docs/sync-strategy.md +0 -533
  18. package/jest.config.js +0 -30
  19. package/scripts/install-deps.sh +0 -38
  20. package/src/commands/apikeys.ts +0 -144
  21. package/src/commands/artifacts.ts +0 -296
  22. package/src/commands/auth.ts +0 -122
  23. package/src/commands/communities.ts +0 -153
  24. package/src/commands/config.ts +0 -144
  25. package/src/commands/conversations.ts +0 -176
  26. package/src/commands/facts.ts +0 -320
  27. package/src/commands/graphrag.ts +0 -149
  28. package/src/commands/memories.ts +0 -332
  29. package/src/commands/patterns.ts +0 -251
  30. package/src/commands/system.ts +0 -102
  31. package/src/commands/topics.ts +0 -354
  32. package/src/index.ts +0 -43
  33. package/src/lib/api-client.ts +0 -68
  34. package/src/lib/auth.ts +0 -42
  35. package/src/lib/config.ts +0 -68
  36. package/src/lib/errors.ts +0 -143
  37. package/src/lib/formatters.ts +0 -123
  38. package/src/lib/spinner.ts +0 -113
  39. package/src/lib/validators.ts +0 -302
  40. package/src/types/index.ts +0 -17
  41. package/tests/__mocks__/chalk.ts +0 -16
  42. package/tests/__mocks__/cli-table3.ts +0 -37
  43. package/tests/__mocks__/configstore.ts +0 -38
  44. package/tests/commands/apikeys.test.ts +0 -179
  45. package/tests/commands/artifacts.test.ts +0 -194
  46. package/tests/commands/auth.test.ts +0 -120
  47. package/tests/commands/communities.test.ts +0 -154
  48. package/tests/commands/config.test.ts +0 -154
  49. package/tests/commands/conversations.test.ts +0 -136
  50. package/tests/commands/facts.test.ts +0 -210
  51. package/tests/commands/graphrag.test.ts +0 -194
  52. package/tests/commands/memories.test.ts +0 -215
  53. package/tests/commands/patterns.test.ts +0 -201
  54. package/tests/commands/system.test.ts +0 -172
  55. package/tests/commands/topics.test.ts +0 -274
  56. package/tests/lib/auth.test.ts +0 -77
  57. package/tests/lib/config.test.ts +0 -50
  58. package/tests/lib/errors.test.ts +0 -126
  59. package/tests/lib/formatters.test.ts +0 -87
  60. package/tsconfig.json +0 -20
@@ -1,332 +0,0 @@
1
- import { Command } from 'commander';
2
- import inquirer from 'inquirer';
3
- import chalk from 'chalk';
4
- import {
5
- listMemories,
6
- getMemoryById,
7
- createMemory,
8
- updateMemory,
9
- deleteMemory,
10
- searchMemories,
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 { createSpinner } from '../lib/spinner';
16
- import { OutputFormat } from '../types';
17
-
18
- /**
19
- * Register memory management commands
20
- * @param program - Commander program instance
21
- */
22
- export function registerMemoriesCommands(program: Command): void {
23
- const memories = program.command('memories').description('Manage memories');
24
-
25
- /**
26
- * mx memories list [options]
27
- * List memories with pagination
28
- */
29
- memories
30
- .command('list')
31
- .description('List memories')
32
- .option('--page <number>', 'Page number', '0')
33
- .option('--limit <number>', 'Results per page', '20')
34
- .option('--format <format>', 'Output format (json|table|yaml)')
35
- .action(async (options) => {
36
- try {
37
- const page = parseInt(options.page, 10);
38
- const limit = parseInt(options.limit, 10);
39
- const offset = page * limit;
40
-
41
- const spinner = createSpinner('Fetching memories...', options.format).start();
42
-
43
- const result = await listMemories({
44
- ...getApiOptions(),
45
- query: { limit, offset },
46
- });
47
-
48
- spinner.succeed(`Found ${result.data?.data?.length || 0} memories`);
49
-
50
- // Format for table display
51
- if (options.format === 'table' || !options.format) {
52
- const tableData =
53
- result.data?.data?.map((m: any) => ({
54
- id: m.id,
55
- content: m.content?.substring(0, 50) + (m.content?.length > 50 ? '...' : ''),
56
- memoryType: m.memoryType || 'N/A',
57
- createdAt: new Date(m.createdAt).toLocaleDateString(),
58
- })) || [];
59
-
60
- printOutput(tableData, options.format as OutputFormat, {
61
- columns: ['id', 'content', 'memoryType', 'createdAt'],
62
- maxColumnWidth: 30,
63
- });
64
-
65
- const pagination = result.data?.pagination;
66
- if (pagination) {
67
- console.log(
68
- chalk.gray(
69
- `\nShowing ${pagination.offset + 1}-${
70
- pagination.offset + (result.data?.data?.length || 0)
71
- } (Page ${page + 1})`
72
- )
73
- );
74
- }
75
- } else {
76
- printOutput(result.data, options.format as OutputFormat);
77
- }
78
- } catch (error) {
79
- handleError(error);
80
- }
81
- });
82
-
83
- /**
84
- * mx memories get <id>
85
- * Get a specific memory by ID
86
- */
87
- memories
88
- .command('get <id>')
89
- .description('Get a specific memory')
90
- .option('--format <format>', 'Output format (json|table|yaml)')
91
- .action(async (id: string, options) => {
92
- try {
93
- const result = await getMemoryById({
94
- ...getApiOptions(),
95
- path: { id },
96
- });
97
- printOutput(result.data, options.format as OutputFormat);
98
- } catch (error) {
99
- handleError(error);
100
- }
101
- });
102
-
103
- /**
104
- * mx memories create [options]
105
- * Create a new memory
106
- */
107
- memories
108
- .command('create')
109
- .description('Create a new memory')
110
- .option('--content <text>', 'Memory content')
111
- .option('--memory-type <type>', 'Memory type (episodic|semantic|procedural)')
112
- .option('--context <context>', 'Context identifier')
113
- .option('--topics <topics>', 'Comma-separated topics')
114
- .option('--importance <number>', 'Importance score (0-1)')
115
- .option('--interactive', 'Interactive mode')
116
- .option('--format <format>', 'Output format (json|table|yaml)')
117
- .action(async (options) => {
118
- try {
119
- let memoryData: any = {};
120
-
121
- if (options.interactive || !options.content) {
122
- // Interactive mode
123
- const answers = await inquirer.prompt([
124
- {
125
- type: 'input',
126
- name: 'content',
127
- message: 'Memory content:',
128
- default: options.content,
129
- validate: (input: string) => input.trim().length > 0 || 'Content is required',
130
- },
131
- {
132
- type: 'list',
133
- name: 'memoryType',
134
- message: 'Memory type:',
135
- choices: ['episodic', 'semantic', 'procedural'],
136
- default: options.memoryType || 'episodic',
137
- },
138
- {
139
- type: 'input',
140
- name: 'context',
141
- message: 'Context (optional):',
142
- default: options.context,
143
- },
144
- {
145
- type: 'input',
146
- name: 'topics',
147
- message: 'Topics (comma-separated, optional):',
148
- default: options.topics,
149
- },
150
- {
151
- type: 'number',
152
- name: 'importance',
153
- message: 'Importance (0-1, optional):',
154
- default: options.importance ? parseFloat(options.importance) : undefined,
155
- },
156
- ]);
157
-
158
- memoryData = {
159
- content: answers.content,
160
- memoryType: answers.memoryType,
161
- context: answers.context || undefined,
162
- topics: answers.topics
163
- ? answers.topics.split(',').map((t: string) => t.trim())
164
- : undefined,
165
- importance: answers.importance || undefined,
166
- };
167
- } else {
168
- // Direct mode
169
- memoryData = {
170
- content: options.content,
171
- memoryType: options.memoryType || 'episodic',
172
- context: options.context,
173
- topics: options.topics
174
- ? options.topics.split(',').map((t: string) => t.trim())
175
- : undefined,
176
- importance: options.importance ? parseFloat(options.importance) : undefined,
177
- };
178
- }
179
-
180
- const spinner = createSpinner('Creating memory...', options.format).start();
181
-
182
- const result = await createMemory({
183
- ...getApiOptions(),
184
- body: memoryData,
185
- });
186
-
187
- const memory = result.data?.data;
188
- spinner.succeed(`Memory created with ID: ${memory?.id}`);
189
- printOutput(memory, options.format as OutputFormat);
190
- } catch (error) {
191
- handleError(error);
192
- }
193
- });
194
-
195
- /**
196
- * mx memories update <id> [options]
197
- * Update an existing memory
198
- */
199
- memories
200
- .command('update <id>')
201
- .description('Update an existing memory')
202
- .option('--content <text>', 'Updated content')
203
- .option('--memory-type <type>', 'Updated memory type')
204
- .option('--context <context>', 'Updated context')
205
- .option('--topics <topics>', 'Updated topics (comma-separated)')
206
- .option('--importance <number>', 'Updated importance (0-1)')
207
- .option('--format <format>', 'Output format (json|table|yaml)')
208
- .action(async (id: string, options) => {
209
- try {
210
- const updates: any = {};
211
-
212
- if (options.content) updates.content = options.content;
213
- if (options.memoryType) updates.memoryType = options.memoryType;
214
- if (options.context) updates.context = options.context;
215
- if (options.topics) updates.topics = options.topics.split(',').map((t: string) => t.trim());
216
- if (options.importance) updates.importance = parseFloat(options.importance);
217
-
218
- if (Object.keys(updates).length === 0) {
219
- console.log(chalk.yellow('No updates provided. Use --help to see available options.'));
220
- return;
221
- }
222
-
223
- const spinner = createSpinner('Updating memory...', options.format).start();
224
-
225
- const result = await updateMemory({
226
- ...getApiOptions(),
227
- path: { id },
228
- body: updates,
229
- });
230
-
231
- const memory = result.data?.data;
232
- spinner.succeed(`Memory ${id} updated successfully`);
233
- printOutput(memory, options.format as OutputFormat);
234
- } catch (error) {
235
- handleError(error);
236
- }
237
- });
238
-
239
- /**
240
- * mx memories delete <id>
241
- * Delete a memory
242
- */
243
- memories
244
- .command('delete <id>')
245
- .description('Delete a memory')
246
- .option('--force', 'Skip confirmation prompt')
247
- .action(async (id: string, options) => {
248
- try {
249
- if (!options.force) {
250
- const answers = await inquirer.prompt([
251
- {
252
- type: 'confirm',
253
- name: 'confirm',
254
- message: `Are you sure you want to delete memory ${id}?`,
255
- default: false,
256
- },
257
- ]);
258
-
259
- if (!answers.confirm) {
260
- console.log(chalk.yellow('Delete cancelled'));
261
- return;
262
- }
263
- }
264
-
265
- const spinner = createSpinner('Deleting memory...', 'table').start();
266
-
267
- await deleteMemory({
268
- ...getApiOptions(),
269
- path: { id },
270
- });
271
-
272
- spinner.succeed(`Memory ${id} deleted successfully`);
273
- } catch (error) {
274
- handleError(error);
275
- }
276
- });
277
-
278
- /**
279
- * mx memories search [options]
280
- * Search memories
281
- */
282
- memories
283
- .command('search')
284
- .description('Search memories')
285
- .option('--query <text>', 'Search query')
286
- .option('--limit <number>', 'Maximum results', '20')
287
- .option('--mode <mode>', 'Search mode (unified|content|facts)', 'unified')
288
- .option('--format <format>', 'Output format (json|table|yaml)')
289
- .action(async (options) => {
290
- try {
291
- if (!options.query) {
292
- console.log(chalk.red('Error: --query is required'));
293
- return;
294
- }
295
-
296
- const spinner = createSpinner('Searching memories...', options.format).start();
297
-
298
- const result = await searchMemories({
299
- ...getApiOptions(),
300
- body: {
301
- query: options.query,
302
- limit: parseInt(options.limit, 10),
303
- mode: options.mode as 'unified' | 'content' | 'facts',
304
- },
305
- });
306
-
307
- const searchData = result.data;
308
- spinner.succeed(`Found ${searchData?.data?.length || 0} results`);
309
-
310
- // Format for table display
311
- if (options.format === 'table' || !options.format) {
312
- const tableData = (searchData?.data || []).map((memory: any) => ({
313
- id: memory.id || 'N/A',
314
- content: memory.content?.substring(0, 50) + (memory.content?.length > 50 ? '...' : ''),
315
- memoryType: memory.memoryType || 'N/A',
316
- createdAt: new Date(memory.createdAt).toLocaleDateString(),
317
- }));
318
-
319
- printOutput(tableData, options.format as OutputFormat, {
320
- columns: ['id', 'content', 'memoryType', 'createdAt'],
321
- maxColumnWidth: 30,
322
- });
323
-
324
- console.log(chalk.gray(`\nFound ${searchData?.count || 0} results`));
325
- } else {
326
- printOutput(searchData, options.format as OutputFormat);
327
- }
328
- } catch (error) {
329
- handleError(error);
330
- }
331
- });
332
- }
@@ -1,251 +0,0 @@
1
- import { Command } from 'commander';
2
- import chalk from 'chalk';
3
- import ora from 'ora';
4
- import {
5
- listPatterns,
6
- compilePatterns,
7
- updatePattern,
8
- recordPatternFeedback,
9
- getBehavioralState,
10
- updateBehavioralState,
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 pattern management commands
19
- * @param program - Commander program instance
20
- */
21
- export function registerPatternsCommands(program: Command): void {
22
- const patterns = program.command('patterns').description('Manage behavioral patterns');
23
-
24
- /**
25
- * mx patterns list [options]
26
- * List patterns with pagination
27
- */
28
- patterns
29
- .command('list')
30
- .description('List patterns')
31
- .option('--page <number>', 'Page number', '0')
32
- .option('--limit <number>', 'Results per page', '20')
33
- .option('--context-id <id>', 'Filter by context')
34
- .option('--type <type>', 'Pattern type (behavioral|temporal|semantic)')
35
- .option('--format <format>', 'Output format (json|table|yaml)')
36
- .action(async (options) => {
37
- try {
38
- const page = parseInt(options.page, 10);
39
- const limit = parseInt(options.limit, 10);
40
- const offset = page * limit;
41
-
42
- const result = await listPatterns({
43
- ...getApiOptions(),
44
- query: {
45
- limit,
46
- offset,
47
- },
48
- });
49
-
50
- const responseData = result.data as any;
51
- if (options.format === 'table' || !options.format) {
52
- const tableData = (responseData?.data || []).map((p: any) => ({
53
- id: p.id,
54
- type: p.type || 'N/A',
55
- description:
56
- p.description?.substring(0, 40) + (p.description?.length > 40 ? '...' : ''),
57
- confidence: p.confidence || 0,
58
- lastUpdated: new Date(p.lastUpdated).toLocaleDateString(),
59
- }));
60
-
61
- printOutput(tableData, options.format as OutputFormat, {
62
- columns: ['id', 'type', 'description', 'confidence', 'lastUpdated'],
63
- maxColumnWidth: 20,
64
- });
65
-
66
- const pagination = responseData?.pagination;
67
- if (pagination) {
68
- console.log(
69
- chalk.gray(
70
- `\nShowing ${pagination.offset + 1}-${
71
- pagination.offset + pagination.count
72
- } (Page ${page + 1})`
73
- )
74
- );
75
- }
76
- } else {
77
- printOutput(responseData, options.format as OutputFormat);
78
- }
79
- } catch (error) {
80
- handleError(error);
81
- }
82
- });
83
-
84
- /**
85
- * mx patterns compile [options]
86
- * Compile patterns from memories
87
- */
88
- patterns
89
- .command('compile')
90
- .description('Compile patterns from memories')
91
- .option('--context-id <id>', 'Context ID (required)')
92
- .option('--force', 'Force recompilation')
93
- .option('--pattern-types <types>', 'Comma-separated pattern types to compile')
94
- .action(async (options) => {
95
- try {
96
- if (!options.contextId) {
97
- console.log(chalk.red('Error: --context-id is required'));
98
- return;
99
- }
100
-
101
- const spinner = ora('Compiling patterns...').start();
102
-
103
- const result = await compilePatterns({
104
- ...getApiOptions(),
105
- body: {
106
- minOccurrences: 2,
107
- timeWindow: '30d',
108
- },
109
- });
110
-
111
- spinner.succeed('Patterns compiled successfully');
112
- printOutput(result.data, 'json');
113
- } catch (error) {
114
- handleError(error);
115
- }
116
- });
117
-
118
- /**
119
- * mx patterns update <id> [options]
120
- * Update a pattern
121
- */
122
- patterns
123
- .command('update <id>')
124
- .description('Update a pattern')
125
- .option('--description <text>', 'Updated description')
126
- .option('--confidence <number>', 'Updated confidence (0-1)')
127
- .option('--active <boolean>', 'Enable/disable pattern')
128
- .option('--metadata <json>', 'Updated metadata')
129
- .option('--format <format>', 'Output format (json|table|yaml)')
130
- .action(async (id: string, options) => {
131
- try {
132
- if (!options.description && !options.confidence && !options.active && !options.metadata) {
133
- console.log(chalk.red('Error: At least one option is required'));
134
- return;
135
- }
136
-
137
- const updates: any = {};
138
-
139
- if (options.description) updates.description = options.description;
140
- if (options.confidence) updates.confidence = parseFloat(options.confidence);
141
- if (options.active) updates.active = options.active === 'true';
142
- if (options.metadata) updates.metadata = JSON.parse(options.metadata);
143
-
144
- const result = await updatePattern({
145
- ...getApiOptions(),
146
- path: { id },
147
- body: updates,
148
- });
149
-
150
- console.log(chalk.green('✓ Pattern updated successfully'));
151
- printOutput(result.data, options.format as OutputFormat);
152
- } catch (error) {
153
- handleError(error);
154
- }
155
- });
156
-
157
- /**
158
- * mx patterns feedback [options]
159
- * Record pattern feedback
160
- */
161
- patterns
162
- .command('feedback')
163
- .description('Record pattern feedback')
164
- .option('--pattern-id <id>', 'Pattern ID (required)')
165
- .option('--feedback-type <type>', 'Feedback type (positive|negative|neutral) (required)')
166
- .option('--comment <text>', 'Feedback comment')
167
- .option('--context-id <id>', 'Context ID')
168
- .action(async (options) => {
169
- try {
170
- if (!options.patternId || !options.feedbackType) {
171
- console.log(chalk.red('Error: --pattern-id and --feedback-type are required'));
172
- return;
173
- }
174
-
175
- const spinner = ora('Recording feedback...').start();
176
-
177
- await recordPatternFeedback({
178
- ...getApiOptions(),
179
- body: {
180
- patternId: options.patternId,
181
- feedback: options.feedbackType,
182
- },
183
- });
184
-
185
- spinner.succeed('Feedback recorded successfully');
186
- console.log(chalk.green('✓ Feedback recorded'));
187
- } catch (error) {
188
- handleError(error);
189
- }
190
- });
191
-
192
- /**
193
- * mx patterns behavior-state [options]
194
- * Get behavioral state
195
- */
196
- patterns
197
- .command('behavior-state')
198
- .description('Get behavioral state')
199
- .option('--context-id <id>', 'Context ID (required)')
200
- .option('--format <format>', 'Output format (json|table|yaml)')
201
- .action(async (options) => {
202
- try {
203
- if (!options.contextId) {
204
- console.log(chalk.red('Error: --context-id is required'));
205
- return;
206
- }
207
-
208
- const result = await getBehavioralState({
209
- ...getApiOptions(),
210
- });
211
-
212
- printOutput(result.data, options.format as OutputFormat);
213
- } catch (error) {
214
- handleError(error);
215
- }
216
- });
217
-
218
- /**
219
- * mx patterns set-behavior-state [options]
220
- * Set behavioral state
221
- */
222
- patterns
223
- .command('set-behavior-state')
224
- .description('Set behavioral state')
225
- .option('--context-id <id>', 'Context ID (required)')
226
- .option('--state <json>', 'Behavioral state JSON (required)')
227
- .option('--merge', 'Merge with existing state instead of replacing')
228
- .action(async (options) => {
229
- try {
230
- if (!options.contextId || !options.state) {
231
- console.log(chalk.red('Error: --context-id and --state are required'));
232
- return;
233
- }
234
-
235
- const spinner = ora('Setting behavioral state...').start();
236
-
237
- const state = JSON.parse(options.state);
238
- await updateBehavioralState({
239
- ...getApiOptions(),
240
- body: {
241
- state,
242
- },
243
- });
244
-
245
- spinner.succeed('Behavioral state set successfully');
246
- console.log(chalk.green('✓ State updated'));
247
- } catch (error) {
248
- handleError(error);
249
- }
250
- });
251
- }
@@ -1,102 +0,0 @@
1
- import { Command } from 'commander';
2
- import chalk from 'chalk';
3
- import ora from 'ora';
4
- import { getSystemHealth, getContextStatus, getFeatureFlags } from '@memnexus-ai/mx-typescript-sdk';
5
- import { getApiOptions } from '../lib/api-client';
6
- import { handleError } from '../lib/errors';
7
- import { printOutput } from '../lib/formatters';
8
- import { OutputFormat } from '../types';
9
-
10
- /**
11
- * Register system management commands
12
- * @param program - Commander program instance
13
- */
14
- export function registerSystemCommands(program: Command): void {
15
- const system = program.command('system').description('System health and statistics');
16
-
17
- /**
18
- * mx system health
19
- * Check system health status
20
- */
21
- system
22
- .command('health')
23
- .description('Check system health')
24
- .option('--format <format>', 'Output format (json|table|yaml)')
25
- .action(async (options) => {
26
- try {
27
- const spinner = ora('Checking system health...').start();
28
-
29
- const result = await getSystemHealth({
30
- ...getApiOptions(),
31
- });
32
-
33
- spinner.succeed('Health check completed');
34
-
35
- const healthData = result.data as any;
36
- if (healthData) {
37
- const status = healthData.status === 'healthy' ? chalk.green('✓') : chalk.red('✗');
38
- console.log(`${status} Status: ${healthData.status}`);
39
-
40
- if (healthData.components) {
41
- console.log(chalk.gray('\nComponent Status:'));
42
- Object.entries(healthData.components).forEach(([name, status]: [string, any]) => {
43
- const icon = status.status === 'healthy' ? chalk.green('✓') : chalk.red('✗');
44
- console.log(` ${icon} ${name}: ${status.status}`);
45
- });
46
- }
47
-
48
- if (options.format && options.format !== 'table') {
49
- printOutput(healthData, options.format as OutputFormat);
50
- }
51
- }
52
- } catch (error) {
53
- handleError(error);
54
- }
55
- });
56
-
57
- /**
58
- * mx system status
59
- * Get detailed system status
60
- */
61
- system
62
- .command('status')
63
- .description('Get system status')
64
- .option('--format <format>', 'Output format (json|table|yaml)')
65
- .action(async (options) => {
66
- try {
67
- const spinner = ora('Fetching system status...').start();
68
-
69
- const result = await getContextStatus({
70
- ...getApiOptions(),
71
- });
72
-
73
- spinner.succeed('Status retrieved');
74
- printOutput(result.data, options.format as OutputFormat);
75
- } catch (error) {
76
- handleError(error);
77
- }
78
- });
79
-
80
- /**
81
- * mx system features
82
- * Get feature flags
83
- */
84
- system
85
- .command('features')
86
- .description('Get feature flags')
87
- .option('--format <format>', 'Output format (json|table|yaml)')
88
- .action(async (options) => {
89
- try {
90
- const spinner = ora('Fetching feature flags...').start();
91
-
92
- const result = await getFeatureFlags({
93
- ...getApiOptions(),
94
- });
95
-
96
- spinner.succeed('Feature flags retrieved');
97
- printOutput(result.data, options.format as OutputFormat);
98
- } catch (error) {
99
- handleError(error);
100
- }
101
- });
102
- }