@memberjunction/cli 2.55.0 → 2.57.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.
package/README.md CHANGED
@@ -72,6 +72,40 @@ module.exports = {
72
72
 
73
73
  ## Commands
74
74
 
75
+ ### `mj sync`
76
+
77
+ Manages MemberJunction metadata synchronization between database and local files. This suite of commands enables version control, IDE-based editing, and CI/CD integration for MJ metadata.
78
+
79
+ ```bash
80
+ mj sync [COMMAND] [OPTIONS]
81
+ ```
82
+
83
+ Available sync commands:
84
+ - `validate` - Validate metadata files for correctness
85
+ - `init` - Initialize a directory for metadata sync
86
+ - `pull` - Pull metadata from database to local files
87
+ - `push` - Push local file changes to database
88
+ - `status` - Show status of local vs database metadata
89
+ - `watch` - Watch for changes and auto-sync
90
+ - `file-reset` - Reset file checksums after manual edits
91
+
92
+ For detailed documentation on metadata sync, see the [MetadataSync README](../MetadataSync/README.md).
93
+
94
+ #### Quick Examples:
95
+ ```bash
96
+ # Validate all metadata files
97
+ mj sync validate
98
+
99
+ # Pull AI Prompts from database
100
+ mj sync pull --entity="AI Prompts"
101
+
102
+ # Push changes to database
103
+ mj sync push
104
+
105
+ # Watch for changes
106
+ mj sync watch
107
+ ```
108
+
75
109
  ### `mj install`
76
110
 
77
111
  Performs a complete installation of MemberJunction, including:
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class FileReset extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ dir: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ sections: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ 'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const core_1 = require("@oclif/core");
30
+ const prompts_1 = require("@inquirer/prompts");
31
+ const ora_classic_1 = __importDefault(require("ora-classic"));
32
+ const chalk_1 = __importDefault(require("chalk"));
33
+ const path = __importStar(require("path"));
34
+ const metadata_sync_1 = require("@memberjunction/metadata-sync");
35
+ class FileReset extends core_1.Command {
36
+ static description = 'Reset file metadata sections';
37
+ static examples = [
38
+ `<%= config.bin %> <%= command.id %>`,
39
+ `<%= config.bin %> <%= command.id %> --dir="ai-prompts"`,
40
+ `<%= config.bin %> <%= command.id %> --sections="dependencies,references"`,
41
+ `<%= config.bin %> <%= command.id %> --all`,
42
+ ];
43
+ static flags = {
44
+ dir: core_1.Flags.string({ description: 'Specific entity directory to reset' }),
45
+ sections: core_1.Flags.string({ description: 'Comma-separated list of sections to reset (dependencies,references,related,lookup)' }),
46
+ all: core_1.Flags.boolean({ description: 'Reset all metadata sections' }),
47
+ 'dry-run': core_1.Flags.boolean({ description: 'Show what would be reset without actually resetting' }),
48
+ force: core_1.Flags.boolean({ description: 'Skip confirmation prompts' }),
49
+ verbose: core_1.Flags.boolean({ char: 'v', description: 'Show detailed output' }),
50
+ };
51
+ async run() {
52
+ const { flags } = await this.parse(FileReset);
53
+ const spinner = (0, ora_classic_1.default)();
54
+ try {
55
+ // Load configuration
56
+ spinner.start('Loading configuration');
57
+ const mjConfig = (0, metadata_sync_1.loadMJConfig)();
58
+ if (!mjConfig) {
59
+ this.error('No mj.config.cjs found in current directory or parent directories');
60
+ }
61
+ // Stop spinner before provider initialization
62
+ spinner.stop();
63
+ // Initialize data provider
64
+ await (0, metadata_sync_1.initializeProvider)(mjConfig);
65
+ // Initialize sync engine
66
+ const syncEngine = await (0, metadata_sync_1.getSyncEngine)((0, metadata_sync_1.getSystemUser)());
67
+ // Show success after initialization
68
+ if (flags.verbose) {
69
+ spinner.succeed('Configuration and metadata loaded');
70
+ }
71
+ else {
72
+ spinner.stop();
73
+ }
74
+ // Determine sections to reset
75
+ let sectionsToReset = [];
76
+ const availableSections = ['primaryKey', 'sync'];
77
+ if (flags.all) {
78
+ sectionsToReset = availableSections;
79
+ }
80
+ else if (flags.sections) {
81
+ sectionsToReset = flags.sections.split(',').map(s => s.trim());
82
+ // Validate sections
83
+ const invalidSections = sectionsToReset.filter(s => !availableSections.includes(s));
84
+ if (invalidSections.length > 0) {
85
+ this.error(`Invalid sections: ${invalidSections.join(', ')}. Valid sections are: ${availableSections.join(', ')}`);
86
+ }
87
+ }
88
+ else {
89
+ // Interactive selection
90
+ sectionsToReset = await (0, prompts_1.checkbox)({
91
+ message: 'Select sections to reset:',
92
+ choices: [
93
+ { name: 'Primary Key metadata', value: 'primaryKey', checked: false },
94
+ { name: 'Sync metadata', value: 'sync', checked: false }
95
+ ]
96
+ });
97
+ if (sectionsToReset.length === 0) {
98
+ this.log(chalk_1.default.yellow('No sections selected. Exiting.'));
99
+ return;
100
+ }
101
+ }
102
+ // Determine target directory
103
+ const targetDir = flags.dir
104
+ ? path.resolve(metadata_sync_1.configManager.getOriginalCwd(), flags.dir)
105
+ : metadata_sync_1.configManager.getOriginalCwd();
106
+ // Show what will be reset
107
+ this.log(chalk_1.default.bold('\nFile Reset Summary:'));
108
+ this.log(`Target directory: ${path.relative(process.cwd(), targetDir)}`);
109
+ this.log(`Sections to reset: ${sectionsToReset.join(', ')}`);
110
+ this.log(`Mode: ${flags['dry-run'] ? 'Dry run (no changes will be made)' : 'Live (files will be modified)'}`);
111
+ // Confirm unless forced or dry-run
112
+ if (!flags.force && !flags['dry-run']) {
113
+ const shouldContinue = await (0, prompts_1.confirm)({
114
+ message: 'This will reset the selected metadata sections. Continue?',
115
+ default: false
116
+ });
117
+ if (!shouldContinue) {
118
+ this.log(chalk_1.default.yellow('\nFile reset cancelled.'));
119
+ return;
120
+ }
121
+ }
122
+ // Create file reset service and execute
123
+ const fileResetService = new metadata_sync_1.FileResetService();
124
+ spinner.start('Resetting file metadata...');
125
+ // Map selected sections to service options
126
+ let serviceSection = 'both';
127
+ if (sectionsToReset.includes('primaryKey') && sectionsToReset.includes('sync')) {
128
+ serviceSection = 'both';
129
+ }
130
+ else if (sectionsToReset.includes('primaryKey')) {
131
+ serviceSection = 'primaryKey';
132
+ }
133
+ else if (sectionsToReset.includes('sync')) {
134
+ serviceSection = 'sync';
135
+ }
136
+ const result = await fileResetService.resetFiles({
137
+ sections: serviceSection,
138
+ dryRun: flags['dry-run'],
139
+ noBackup: false,
140
+ verbose: flags.verbose
141
+ }, {
142
+ onProgress: (message) => {
143
+ spinner.start(message);
144
+ },
145
+ onWarn: (message) => {
146
+ this.warn(message);
147
+ },
148
+ onLog: (message) => {
149
+ this.log(message);
150
+ }
151
+ });
152
+ spinner.stop();
153
+ // Show summary
154
+ if (result.modifiedFiles > 0) {
155
+ if (flags['dry-run']) {
156
+ this.log(chalk_1.default.blue(`\n🔍 Dry run complete. ${result.modifiedFiles} file(s) would be reset.`));
157
+ }
158
+ else {
159
+ this.log(chalk_1.default.green(`\n✅ Successfully reset ${result.modifiedFiles} file(s).`));
160
+ if (result.backupsCreated > 0) {
161
+ this.log(chalk_1.default.gray(` 📁 Created ${result.backupsCreated} backup(s).`));
162
+ }
163
+ }
164
+ }
165
+ else {
166
+ this.log(chalk_1.default.yellow('\n⚠️ No files needed resetting.'));
167
+ }
168
+ if (flags.verbose) {
169
+ this.log(`\n📊 Statistics:`);
170
+ this.log(` Files processed: ${result.processedFiles}`);
171
+ this.log(` Files with primaryKey: ${result.filesWithPrimaryKey}`);
172
+ this.log(` Files with sync: ${result.filesWithSync}`);
173
+ this.log(` Total primaryKeys removed: ${result.totalPrimaryKeys}`);
174
+ this.log(` Total syncs removed: ${result.totalSyncs}`);
175
+ }
176
+ }
177
+ catch (error) {
178
+ spinner.fail('File reset failed');
179
+ // Enhanced error logging
180
+ this.log('\n=== File Reset Error Details ===');
181
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
182
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
183
+ if (error instanceof Error && error.stack) {
184
+ this.log(`\nStack trace:`);
185
+ this.log(error.stack);
186
+ }
187
+ this.error(error);
188
+ }
189
+ finally {
190
+ // Reset singletons
191
+ (0, metadata_sync_1.resetSyncEngine)();
192
+ // Exit process
193
+ process.exit(0);
194
+ }
195
+ }
196
+ }
197
+ exports.default = FileReset;
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Init extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@oclif/core");
7
+ const prompts_1 = require("@inquirer/prompts");
8
+ const ora_classic_1 = __importDefault(require("ora-classic"));
9
+ const metadata_sync_1 = require("@memberjunction/metadata-sync");
10
+ class Init extends core_1.Command {
11
+ static description = 'Initialize a directory for metadata synchronization';
12
+ static examples = [
13
+ `<%= config.bin %> <%= command.id %>`,
14
+ ];
15
+ async run() {
16
+ const spinner = (0, ora_classic_1.default)();
17
+ try {
18
+ // Check if already initialized
19
+ const initService = new metadata_sync_1.InitService();
20
+ // Build options from user input
21
+ const options = {};
22
+ // Check for existing configuration
23
+ try {
24
+ // If .mj-sync.json exists, ask about overwrite
25
+ const overwrite = await (0, prompts_1.select)({
26
+ message: 'Directory already initialized. Overwrite configuration?',
27
+ choices: [
28
+ { name: 'Yes', value: true },
29
+ { name: 'No', value: false }
30
+ ]
31
+ });
32
+ if (!overwrite) {
33
+ this.log('Initialization cancelled');
34
+ return;
35
+ }
36
+ options.overwrite = true;
37
+ }
38
+ catch {
39
+ // File doesn't exist, proceed normally
40
+ }
41
+ // Ask if they want to set up an entity directory
42
+ const setupEntity = await (0, prompts_1.select)({
43
+ message: 'Would you like to set up an entity directory now?',
44
+ choices: [
45
+ { name: 'Yes - AI Prompts', value: 'ai-prompts' },
46
+ { name: 'Yes - Other entity', value: 'other' },
47
+ { name: 'No - I\'ll set up later', value: 'no' }
48
+ ]
49
+ });
50
+ options.setupEntity = setupEntity;
51
+ if (setupEntity === 'other') {
52
+ options.entityName = await (0, prompts_1.input)({
53
+ message: 'Enter the entity name (e.g., "Templates", "AI Models"):'
54
+ });
55
+ options.dirName = await (0, prompts_1.input)({
56
+ message: 'Enter the directory name:',
57
+ default: options.entityName.toLowerCase().replace(/\s+/g, '-')
58
+ });
59
+ }
60
+ // Initialize with callbacks
61
+ await initService.initialize(options, {
62
+ onProgress: (message) => {
63
+ spinner.start(message);
64
+ },
65
+ onSuccess: (message) => {
66
+ spinner.succeed(message);
67
+ },
68
+ onError: (message) => {
69
+ spinner.fail(message);
70
+ }
71
+ });
72
+ this.log('\n✅ Initialization complete!');
73
+ // Show next steps
74
+ const nextSteps = initService.getNextSteps();
75
+ this.log('\nNext steps:');
76
+ nextSteps.forEach((step, index) => {
77
+ this.log(`${index + 1}. ${step.replace('mj-sync', 'mj sync')}`);
78
+ });
79
+ }
80
+ catch (error) {
81
+ spinner.fail('Initialization failed');
82
+ // Enhanced error logging
83
+ this.log('\n=== Initialization Error Details ===');
84
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
85
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
86
+ if (error instanceof Error && error.stack) {
87
+ this.log(`\nStack trace:`);
88
+ this.log(error.stack);
89
+ }
90
+ // Check for error hints
91
+ if (error instanceof Error) {
92
+ const hint = new metadata_sync_1.InitService().getErrorHint(error);
93
+ if (hint) {
94
+ this.log(`\nHint: ${hint}`);
95
+ }
96
+ }
97
+ this.error(error);
98
+ }
99
+ }
100
+ }
101
+ exports.default = Init;
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Pull extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ entity: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ filter: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ 'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ 'multi-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ 'no-validate': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ private findEntityDirectories;
15
+ }
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const core_1 = require("@oclif/core");
30
+ const prompts_1 = require("@inquirer/prompts");
31
+ const ora_classic_1 = __importDefault(require("ora-classic"));
32
+ const chalk_1 = __importDefault(require("chalk"));
33
+ const metadata_sync_1 = require("@memberjunction/metadata-sync");
34
+ class Pull extends core_1.Command {
35
+ static description = 'Pull metadata from database to local files';
36
+ static examples = [
37
+ `<%= config.bin %> <%= command.id %> --entity="AI Prompts"`,
38
+ `<%= config.bin %> <%= command.id %> --entity="AI Prompts" --filter="CategoryID='customer-service-id'"`,
39
+ ];
40
+ static flags = {
41
+ entity: core_1.Flags.string({ description: 'Entity name to pull', required: true }),
42
+ filter: core_1.Flags.string({ description: 'Additional filter for pulling specific records' }),
43
+ 'dry-run': core_1.Flags.boolean({ description: 'Show what would be pulled without actually pulling' }),
44
+ 'multi-file': core_1.Flags.string({ description: 'Create a single file with multiple records (provide filename)' }),
45
+ verbose: core_1.Flags.boolean({ char: 'v', description: 'Show detailed output' }),
46
+ 'no-validate': core_1.Flags.boolean({ description: 'Skip validation before pull' }),
47
+ };
48
+ async run() {
49
+ const { flags } = await this.parse(Pull);
50
+ const spinner = (0, ora_classic_1.default)();
51
+ try {
52
+ // Load MJ config first
53
+ spinner.start('Loading configuration');
54
+ const mjConfig = (0, metadata_sync_1.loadMJConfig)();
55
+ if (!mjConfig) {
56
+ this.error('No mj.config.cjs found in current directory or parent directories');
57
+ }
58
+ // Stop spinner before provider initialization
59
+ spinner.stop();
60
+ // Initialize data provider
61
+ await (0, metadata_sync_1.initializeProvider)(mjConfig);
62
+ // Get singleton sync engine
63
+ const syncEngine = await (0, metadata_sync_1.getSyncEngine)((0, metadata_sync_1.getSystemUser)());
64
+ // Show success after initialization
65
+ if (flags.verbose) {
66
+ spinner.succeed('Configuration and metadata loaded');
67
+ }
68
+ else {
69
+ spinner.stop();
70
+ }
71
+ // Run validation unless disabled
72
+ if (!flags['no-validate']) {
73
+ spinner.start('Validating metadata...');
74
+ const validator = new metadata_sync_1.ValidationService({ verbose: flags.verbose });
75
+ const formatter = new metadata_sync_1.FormattingService();
76
+ const validationResult = await validator.validateDirectory(metadata_sync_1.configManager.getOriginalCwd());
77
+ spinner.stop();
78
+ if (!validationResult.isValid || validationResult.warnings.length > 0) {
79
+ // Show validation results
80
+ this.log('\n' + formatter.formatValidationResult(validationResult, flags.verbose));
81
+ if (!validationResult.isValid) {
82
+ // Ask for confirmation
83
+ const shouldContinue = await (0, prompts_1.select)({
84
+ message: 'Validation failed with errors. Do you want to continue anyway?',
85
+ choices: [
86
+ { name: 'No, fix the errors first', value: false },
87
+ { name: 'Yes, continue anyway', value: true }
88
+ ],
89
+ default: false
90
+ });
91
+ if (!shouldContinue) {
92
+ this.error('Pull cancelled due to validation errors.');
93
+ }
94
+ }
95
+ }
96
+ else {
97
+ this.log(chalk_1.default.green('✓ Validation passed'));
98
+ }
99
+ }
100
+ // Set target directory if specified via environment
101
+ const envTargetDir = process.env.METADATA_SYNC_TARGET_DIR;
102
+ // Create pull service and execute
103
+ const pullService = new metadata_sync_1.PullService(syncEngine, (0, metadata_sync_1.getSystemUser)());
104
+ try {
105
+ const result = await pullService.pull({
106
+ entity: flags.entity,
107
+ filter: flags.filter,
108
+ dryRun: flags['dry-run'],
109
+ multiFile: flags['multi-file'],
110
+ verbose: flags.verbose,
111
+ noValidate: flags['no-validate'],
112
+ targetDir: envTargetDir
113
+ }, {
114
+ onProgress: (message) => {
115
+ spinner.start(message);
116
+ },
117
+ onSuccess: (message) => {
118
+ spinner.succeed(message);
119
+ },
120
+ onError: (message) => {
121
+ spinner.fail(message);
122
+ },
123
+ onWarn: (message) => {
124
+ this.warn(message);
125
+ },
126
+ onLog: (message) => {
127
+ this.log(message);
128
+ }
129
+ });
130
+ if (!flags['dry-run']) {
131
+ this.log(`\n✅ Pull completed successfully`);
132
+ }
133
+ }
134
+ catch (pullError) {
135
+ // If it's a "multiple directories found" error, handle it specially
136
+ if (pullError instanceof Error && pullError.message.includes('Multiple directories found')) {
137
+ // Re-throw to be caught by outer handler
138
+ throw pullError;
139
+ }
140
+ throw pullError;
141
+ }
142
+ }
143
+ catch (error) {
144
+ spinner.fail('Pull failed');
145
+ // Handle multiple directories error
146
+ if (error instanceof Error && error.message.includes('Multiple directories found')) {
147
+ const entityDirs = await this.findEntityDirectories(flags.entity);
148
+ const targetDir = await (0, prompts_1.select)({
149
+ message: `Multiple directories found for entity "${flags.entity}". Which one to use?`,
150
+ choices: entityDirs.map(dir => ({ name: dir, value: dir }))
151
+ });
152
+ // Re-run with specific target directory
153
+ process.env.METADATA_SYNC_TARGET_DIR = targetDir;
154
+ await this.run();
155
+ return;
156
+ }
157
+ // Enhanced error logging
158
+ this.log('\n=== Pull Error Details ===');
159
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
160
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
161
+ if (error instanceof Error && error.stack) {
162
+ this.log(`\nStack trace:`);
163
+ this.log(error.stack);
164
+ }
165
+ // Context information
166
+ this.log(`\nContext:`);
167
+ this.log(`- Working directory: ${metadata_sync_1.configManager.getOriginalCwd()}`);
168
+ this.log(`- Entity: ${flags.entity || 'not specified'}`);
169
+ this.log(`- Filter: ${flags.filter || 'none'}`);
170
+ this.error(error);
171
+ }
172
+ finally {
173
+ // Reset singletons
174
+ (0, metadata_sync_1.resetSyncEngine)();
175
+ // Exit process to prevent background MJ tasks from throwing errors
176
+ process.exit(0);
177
+ }
178
+ }
179
+ async findEntityDirectories(entityName) {
180
+ // This is a simplified version - would need full implementation
181
+ const { findEntityDirectories } = await Promise.resolve().then(() => __importStar(require('@memberjunction/metadata-sync')));
182
+ return findEntityDirectories(metadata_sync_1.configManager.getOriginalCwd());
183
+ }
184
+ }
185
+ exports.default = Pull;
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Push extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ dir: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ 'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ ci: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ 'no-validate': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ }