@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.
@@ -0,0 +1,183 @@
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 chalk_1 = __importDefault(require("chalk"));
10
+ const metadata_sync_1 = require("@memberjunction/metadata-sync");
11
+ const path_1 = __importDefault(require("path"));
12
+ class Push extends core_1.Command {
13
+ static description = 'Push local file changes to the database';
14
+ static examples = [
15
+ `<%= config.bin %> <%= command.id %>`,
16
+ `<%= config.bin %> <%= command.id %> --dry-run`,
17
+ `<%= config.bin %> <%= command.id %> --dir="ai-prompts"`,
18
+ `<%= config.bin %> <%= command.id %> --ci`,
19
+ ];
20
+ static flags = {
21
+ dir: core_1.Flags.string({ description: 'Specific entity directory to push' }),
22
+ 'dry-run': core_1.Flags.boolean({ description: 'Show what would be pushed without actually pushing' }),
23
+ ci: core_1.Flags.boolean({ description: 'CI mode - no prompts, fail on issues' }),
24
+ verbose: core_1.Flags.boolean({ char: 'v', description: 'Show detailed field-level output' }),
25
+ 'no-validate': core_1.Flags.boolean({ description: 'Skip validation before push' }),
26
+ };
27
+ async run() {
28
+ const { flags } = await this.parse(Push);
29
+ const spinner = (0, ora_classic_1.default)();
30
+ const startTime = Date.now();
31
+ try {
32
+ // Load configurations
33
+ spinner.start('Loading configuration');
34
+ const mjConfig = (0, metadata_sync_1.loadMJConfig)();
35
+ if (!mjConfig) {
36
+ this.error('No mj.config.cjs found in current directory or parent directories');
37
+ }
38
+ // Load sync config (currently only used internally by PushService)
39
+ const syncConfigDir = flags.dir ? path_1.default.resolve(metadata_sync_1.configManager.getOriginalCwd(), flags.dir) : metadata_sync_1.configManager.getOriginalCwd();
40
+ await (0, metadata_sync_1.loadSyncConfig)(syncConfigDir);
41
+ // Stop spinner before provider initialization
42
+ spinner.stop();
43
+ // Initialize data provider
44
+ await (0, metadata_sync_1.initializeProvider)(mjConfig);
45
+ // Initialize sync engine
46
+ const syncEngine = await (0, metadata_sync_1.getSyncEngine)((0, metadata_sync_1.getSystemUser)());
47
+ // Show success after initialization
48
+ if (flags.verbose) {
49
+ spinner.succeed('Configuration and metadata loaded');
50
+ }
51
+ else {
52
+ spinner.stop();
53
+ }
54
+ // Run validation unless disabled
55
+ if (!flags['no-validate']) {
56
+ spinner.start('Validating metadata...');
57
+ const validator = new metadata_sync_1.ValidationService({ verbose: flags.verbose });
58
+ const formatter = new metadata_sync_1.FormattingService();
59
+ const targetDir = flags.dir ? path_1.default.resolve(metadata_sync_1.configManager.getOriginalCwd(), flags.dir) : metadata_sync_1.configManager.getOriginalCwd();
60
+ const validationResult = await validator.validateDirectory(targetDir);
61
+ spinner.stop();
62
+ if (!validationResult.isValid || validationResult.warnings.length > 0) {
63
+ // Show validation results
64
+ this.log('\n' + formatter.formatValidationResult(validationResult, flags.verbose));
65
+ if (!validationResult.isValid) {
66
+ // In CI mode, fail immediately
67
+ if (flags.ci) {
68
+ this.error('Validation failed. Cannot proceed with push.');
69
+ }
70
+ // Otherwise, ask for confirmation
71
+ const shouldContinue = await (0, prompts_1.confirm)({
72
+ message: 'Validation failed with errors. Do you want to continue anyway?',
73
+ default: false,
74
+ });
75
+ if (!shouldContinue) {
76
+ this.log(chalk_1.default.yellow('\n⚠️ Push cancelled due to validation errors.'));
77
+ // Exit cleanly without throwing an error
78
+ return;
79
+ }
80
+ }
81
+ }
82
+ else {
83
+ this.log(chalk_1.default.green('✓ Validation passed'));
84
+ }
85
+ }
86
+ // Create push service and execute
87
+ const pushService = new metadata_sync_1.PushService(syncEngine, (0, metadata_sync_1.getSystemUser)());
88
+ const result = await pushService.push({
89
+ dir: flags.dir,
90
+ dryRun: flags['dry-run'],
91
+ verbose: flags.verbose,
92
+ noValidate: flags['no-validate'],
93
+ }, {
94
+ onProgress: (message) => {
95
+ spinner.start(message);
96
+ },
97
+ onSuccess: (message) => {
98
+ spinner.succeed(message);
99
+ },
100
+ onError: (message) => {
101
+ this.error(message);
102
+ },
103
+ onWarn: (message) => {
104
+ this.warn(message);
105
+ },
106
+ onLog: (message) => {
107
+ this.log(message);
108
+ },
109
+ onConfirm: async (message) => {
110
+ if (flags.ci) {
111
+ return true; // Always confirm in CI mode
112
+ }
113
+ return await (0, prompts_1.confirm)({ message });
114
+ },
115
+ });
116
+ // Show summary
117
+ const endTime = Date.now();
118
+ const formatter = new metadata_sync_1.FormattingService();
119
+ const summary = formatter.formatSyncSummary('push', {
120
+ created: result.created,
121
+ updated: result.updated,
122
+ unchanged: result.unchanged,
123
+ deleted: 0,
124
+ skipped: 0,
125
+ errors: result.errors,
126
+ duration: endTime - startTime,
127
+ });
128
+ this.log('\n' + summary);
129
+ // Handle result
130
+ if (result.errors > 0) {
131
+ if (flags.ci) {
132
+ this.error('Push failed with errors in CI mode');
133
+ }
134
+ const shouldContinue = await (0, prompts_1.confirm)({
135
+ message: 'Push completed with errors. Do you want to commit the successful changes?',
136
+ default: false,
137
+ });
138
+ if (!shouldContinue) {
139
+ throw new Error('Push cancelled due to errors');
140
+ }
141
+ }
142
+ // Show warnings summary
143
+ if (result.warnings.length > 0) {
144
+ this.log(chalk_1.default.yellow(`\n⚠️ ${result.warnings.length} warning${result.warnings.length > 1 ? 's' : ''} during push`));
145
+ if (flags.verbose) {
146
+ result.warnings.forEach((warning) => this.log(` - ${warning}`));
147
+ }
148
+ }
149
+ // Show final status
150
+ if (!flags['dry-run']) {
151
+ if (result.errors === 0) {
152
+ this.log(chalk_1.default.green('\n✅ Push completed successfully'));
153
+ }
154
+ else {
155
+ this.log(chalk_1.default.yellow('\n⚠️ Push completed with errors'));
156
+ }
157
+ if (result.sqlLogPath) {
158
+ this.log(`\n📄 SQL log saved to: ${path_1.default.relative(process.cwd(), result.sqlLogPath)}`);
159
+ }
160
+ }
161
+ }
162
+ catch (error) {
163
+ spinner.fail('Push failed');
164
+ // Error handling - individual operations may have partially succeeded
165
+ // Enhanced error logging
166
+ this.log('\n=== Push Error Details ===');
167
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
168
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
169
+ if (error instanceof Error && error.stack) {
170
+ this.log(`\nStack trace:`);
171
+ this.log(error.stack);
172
+ }
173
+ this.error(error);
174
+ }
175
+ finally {
176
+ // Reset singletons
177
+ (0, metadata_sync_1.resetSyncEngine)();
178
+ // Exit process
179
+ process.exit(0);
180
+ }
181
+ }
182
+ }
183
+ exports.default = Push;
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Status 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
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,112 @@
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 ora_classic_1 = __importDefault(require("ora-classic"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const metadata_sync_1 = require("@memberjunction/metadata-sync");
10
+ class Status extends core_1.Command {
11
+ static description = 'Show status of local files vs database';
12
+ static examples = [
13
+ `<%= config.bin %> <%= command.id %>`,
14
+ `<%= config.bin %> <%= command.id %> --dir="ai-prompts"`,
15
+ `<%= config.bin %> <%= command.id %> --verbose`,
16
+ ];
17
+ static flags = {
18
+ dir: core_1.Flags.string({ description: 'Specific entity directory to check status' }),
19
+ verbose: core_1.Flags.boolean({ char: 'v', description: 'Show detailed field-level differences' }),
20
+ };
21
+ async run() {
22
+ const { flags } = await this.parse(Status);
23
+ const spinner = (0, ora_classic_1.default)();
24
+ try {
25
+ // Load configuration
26
+ spinner.start('Loading configuration');
27
+ const mjConfig = (0, metadata_sync_1.loadMJConfig)();
28
+ if (!mjConfig) {
29
+ this.error('No mj.config.cjs found in current directory or parent directories');
30
+ }
31
+ // Stop spinner before provider initialization
32
+ spinner.stop();
33
+ // Initialize data provider
34
+ await (0, metadata_sync_1.initializeProvider)(mjConfig);
35
+ // Initialize sync engine
36
+ const syncEngine = await (0, metadata_sync_1.getSyncEngine)((0, metadata_sync_1.getSystemUser)());
37
+ // Show success after initialization
38
+ if (flags.verbose) {
39
+ spinner.succeed('Configuration and metadata loaded');
40
+ }
41
+ else {
42
+ spinner.stop();
43
+ }
44
+ // Create status service and execute
45
+ const statusService = new metadata_sync_1.StatusService(syncEngine);
46
+ spinner.start('Checking status...');
47
+ const result = await statusService.checkStatus({
48
+ dir: flags.dir
49
+ }, {
50
+ onProgress: (message) => {
51
+ spinner.start(message);
52
+ },
53
+ onWarn: (message) => {
54
+ this.warn(message);
55
+ },
56
+ onLog: (message) => {
57
+ this.log(message);
58
+ }
59
+ });
60
+ spinner.stop();
61
+ // Display results
62
+ // Show summary
63
+ const summary = result.summary;
64
+ const totalFiles = summary.new + summary.modified + summary.deleted + summary.unchanged;
65
+ if (totalFiles === 0) {
66
+ this.log(chalk_1.default.yellow('\nNo metadata files found in the specified directory.'));
67
+ }
68
+ else {
69
+ this.log(chalk_1.default.bold('\nSummary:'));
70
+ if (summary.new > 0) {
71
+ this.log(chalk_1.default.green(` ${summary.new} new file(s) to push`));
72
+ }
73
+ if (summary.modified > 0) {
74
+ this.log(chalk_1.default.yellow(` ${summary.modified} modified file(s) to push`));
75
+ }
76
+ if (summary.deleted > 0) {
77
+ this.log(chalk_1.default.red(` ${summary.deleted} deleted file(s) to remove`));
78
+ }
79
+ if (summary.unchanged > 0) {
80
+ this.log(chalk_1.default.gray(` ${summary.unchanged} unchanged file(s)`));
81
+ }
82
+ // Show details by entity if verbose
83
+ if (flags.verbose && result.details.length > 0) {
84
+ this.log(chalk_1.default.bold('\nDetails by entity:'));
85
+ for (const detail of result.details) {
86
+ this.log(`\n ${detail.entityName} (${detail.directory}):`);
87
+ this.log(` New: ${detail.new}, Modified: ${detail.modified}, Deleted: ${detail.deleted}, Unchanged: ${detail.unchanged}`);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ catch (error) {
93
+ spinner.fail('Status check failed');
94
+ // Enhanced error logging
95
+ this.log('\n=== Status Error Details ===');
96
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
97
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
98
+ if (error instanceof Error && error.stack) {
99
+ this.log(`\nStack trace:`);
100
+ this.log(error.stack);
101
+ }
102
+ this.error(error);
103
+ }
104
+ finally {
105
+ // Reset singletons
106
+ (0, metadata_sync_1.resetSyncEngine)();
107
+ // Exit process
108
+ process.exit(0);
109
+ }
110
+ }
111
+ }
112
+ exports.default = Status;
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Validate 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
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ 'save-report': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ output: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,131 @@
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 ora_classic_1 = __importDefault(require("ora-classic"));
31
+ const chalk_1 = __importDefault(require("chalk"));
32
+ const path = __importStar(require("path"));
33
+ const fs_extra_1 = __importDefault(require("fs-extra"));
34
+ const metadata_sync_1 = require("@memberjunction/metadata-sync");
35
+ class Validate extends core_1.Command {
36
+ static description = 'Validate metadata files';
37
+ static examples = [
38
+ `<%= config.bin %> <%= command.id %>`,
39
+ `<%= config.bin %> <%= command.id %> --dir="ai-prompts"`,
40
+ `<%= config.bin %> <%= command.id %> --save-report`,
41
+ `<%= config.bin %> <%= command.id %> --verbose`,
42
+ ];
43
+ static flags = {
44
+ dir: core_1.Flags.string({ description: 'Specific entity directory to validate' }),
45
+ verbose: core_1.Flags.boolean({ char: 'v', description: 'Show detailed validation output' }),
46
+ 'save-report': core_1.Flags.boolean({ description: 'Save validation report as markdown file' }),
47
+ 'output': core_1.Flags.string({ description: 'Output file path for validation report (default: validation-report.md)' }),
48
+ };
49
+ async run() {
50
+ const { flags } = await this.parse(Validate);
51
+ const spinner = (0, ora_classic_1.default)();
52
+ try {
53
+ // Load configuration
54
+ spinner.start('Loading configuration');
55
+ const mjConfig = (0, metadata_sync_1.loadMJConfig)();
56
+ if (!mjConfig) {
57
+ this.error('No mj.config.cjs found in current directory or parent directories');
58
+ }
59
+ // Stop spinner before provider initialization
60
+ spinner.stop();
61
+ // Initialize data provider
62
+ await (0, metadata_sync_1.initializeProvider)(mjConfig);
63
+ // Initialize sync engine (needed for metadata access)
64
+ await (0, metadata_sync_1.getSyncEngine)((0, metadata_sync_1.getSystemUser)());
65
+ // Show success after initialization
66
+ if (flags.verbose) {
67
+ spinner.succeed('Configuration and metadata loaded');
68
+ }
69
+ else {
70
+ spinner.stop();
71
+ }
72
+ // Create validation service
73
+ const validator = new metadata_sync_1.ValidationService({ verbose: flags.verbose });
74
+ const formatter = new metadata_sync_1.FormattingService();
75
+ // Determine directory to validate
76
+ const targetDir = flags.dir
77
+ ? path.resolve(metadata_sync_1.configManager.getOriginalCwd(), flags.dir)
78
+ : metadata_sync_1.configManager.getOriginalCwd();
79
+ spinner.start(`Validating metadata in ${path.relative(process.cwd(), targetDir)}...`);
80
+ // Run validation
81
+ const validationResult = await validator.validateDirectory(targetDir);
82
+ spinner.stop();
83
+ // Format and display results
84
+ const formattedResult = formatter.formatValidationResult(validationResult, flags.verbose);
85
+ this.log('\n' + formattedResult);
86
+ // Save report if requested
87
+ if (flags['save-report']) {
88
+ const reportPath = flags.output || 'validation-report.md';
89
+ const fullReportPath = path.resolve(reportPath);
90
+ // Generate markdown report
91
+ const report = formatter.formatValidationResultAsMarkdown(validationResult);
92
+ await fs_extra_1.default.writeFile(fullReportPath, report, 'utf8');
93
+ this.log(chalk_1.default.green(`\n✅ Validation report saved to: ${path.relative(process.cwd(), fullReportPath)}`));
94
+ }
95
+ // Show summary
96
+ const totalIssues = validationResult.errors.length + validationResult.warnings.length;
97
+ if (validationResult.isValid && validationResult.warnings.length === 0) {
98
+ this.log(chalk_1.default.green('\n✅ All metadata files are valid!'));
99
+ }
100
+ else if (validationResult.isValid && validationResult.warnings.length > 0) {
101
+ this.log(chalk_1.default.yellow(`\n⚠️ Validation passed with ${validationResult.warnings.length} warning(s)`));
102
+ }
103
+ else {
104
+ this.log(chalk_1.default.red(`\n❌ Validation failed with ${validationResult.errors.length} error(s) and ${validationResult.warnings.length} warning(s)`));
105
+ }
106
+ // Exit with error code if validation failed
107
+ if (!validationResult.isValid) {
108
+ process.exit(1);
109
+ }
110
+ }
111
+ catch (error) {
112
+ spinner.fail('Validation failed');
113
+ // Enhanced error logging
114
+ this.log('\n=== Validation Error Details ===');
115
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
116
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
117
+ if (error instanceof Error && error.stack) {
118
+ this.log(`\nStack trace:`);
119
+ this.log(error.stack);
120
+ }
121
+ this.error(error);
122
+ }
123
+ finally {
124
+ // Reset singletons
125
+ (0, metadata_sync_1.resetSyncEngine)();
126
+ // Exit process
127
+ process.exit(0);
128
+ }
129
+ }
130
+ }
131
+ exports.default = Validate;
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Watch 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
+ debounce: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ 'no-validate': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ private watchController?;
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,148 @@
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 ora_classic_1 = __importDefault(require("ora-classic"));
31
+ const chalk_1 = __importDefault(require("chalk"));
32
+ const path = __importStar(require("path"));
33
+ const metadata_sync_1 = require("@memberjunction/metadata-sync");
34
+ class Watch extends core_1.Command {
35
+ static description = 'Watch for file changes and sync automatically';
36
+ static examples = [
37
+ `<%= config.bin %> <%= command.id %>`,
38
+ `<%= config.bin %> <%= command.id %> --dir="ai-prompts"`,
39
+ `<%= config.bin %> <%= command.id %> --debounce=1000`,
40
+ `<%= config.bin %> <%= command.id %> --no-validate`,
41
+ ];
42
+ static flags = {
43
+ dir: core_1.Flags.string({ description: 'Specific entity directory to watch' }),
44
+ debounce: core_1.Flags.integer({ description: 'Debounce delay in milliseconds (default: 500)' }),
45
+ 'no-validate': core_1.Flags.boolean({ description: 'Skip validation before sync' }),
46
+ verbose: core_1.Flags.boolean({ char: 'v', description: 'Show detailed output' }),
47
+ };
48
+ watchController;
49
+ async run() {
50
+ const { flags } = await this.parse(Watch);
51
+ const spinner = (0, ora_classic_1.default)();
52
+ try {
53
+ // Load configuration
54
+ spinner.start('Loading configuration');
55
+ const mjConfig = (0, metadata_sync_1.loadMJConfig)();
56
+ if (!mjConfig) {
57
+ this.error('No mj.config.cjs found in current directory or parent directories');
58
+ }
59
+ // Load sync config
60
+ const syncConfigDir = flags.dir ? path.resolve(metadata_sync_1.configManager.getOriginalCwd(), flags.dir) : metadata_sync_1.configManager.getOriginalCwd();
61
+ const syncConfig = await (0, metadata_sync_1.loadSyncConfig)(syncConfigDir);
62
+ // Stop spinner before provider initialization
63
+ spinner.stop();
64
+ // Initialize data provider
65
+ await (0, metadata_sync_1.initializeProvider)(mjConfig);
66
+ // Initialize sync engine
67
+ const syncEngine = await (0, metadata_sync_1.getSyncEngine)((0, metadata_sync_1.getSystemUser)());
68
+ // Show success after initialization
69
+ spinner.succeed('Configuration and metadata loaded');
70
+ // Determine watch directory
71
+ const watchDir = flags.dir
72
+ ? path.resolve(metadata_sync_1.configManager.getOriginalCwd(), flags.dir)
73
+ : metadata_sync_1.configManager.getOriginalCwd();
74
+ // Create watch service
75
+ const watchService = new metadata_sync_1.WatchService(syncEngine);
76
+ // Setup graceful shutdown
77
+ process.on('SIGINT', async () => {
78
+ this.log(chalk_1.default.yellow('\n\n⏹️ Stopping file watcher...'));
79
+ if (this.watchController) {
80
+ await this.watchController.stop();
81
+ }
82
+ (0, metadata_sync_1.resetSyncEngine)();
83
+ process.exit(0);
84
+ });
85
+ process.on('SIGTERM', async () => {
86
+ if (this.watchController) {
87
+ await this.watchController.stop();
88
+ }
89
+ (0, metadata_sync_1.resetSyncEngine)();
90
+ process.exit(0);
91
+ });
92
+ // Start watching
93
+ this.log(chalk_1.default.bold(`\n👀 Watching for changes in: ${path.relative(process.cwd(), watchDir)}`));
94
+ this.log(chalk_1.default.gray('Press Ctrl+C to stop watching\n'));
95
+ this.watchController = await watchService.watch({
96
+ dir: flags.dir,
97
+ debounceMs: flags.debounce || 500
98
+ }, {
99
+ onFileAdd: (filePath, entityDir, entityConfig) => {
100
+ const relativePath = path.relative(process.cwd(), filePath);
101
+ this.log(chalk_1.default.gray(`➕ added: ${relativePath}`));
102
+ },
103
+ onFileChange: (filePath, entityDir, entityConfig) => {
104
+ const relativePath = path.relative(process.cwd(), filePath);
105
+ this.log(chalk_1.default.gray(`📝 changed: ${relativePath}`));
106
+ },
107
+ onFileDelete: (filePath, entityDir, entityConfig) => {
108
+ const relativePath = path.relative(process.cwd(), filePath);
109
+ this.log(chalk_1.default.gray(`❌ deleted: ${relativePath}`));
110
+ },
111
+ onRecordSaved: (entity, isNew, entityConfig) => {
112
+ const action = isNew ? 'created' : 'updated';
113
+ this.log(chalk_1.default.green(`✅ Record ${action} for ${entityConfig.entity}`));
114
+ },
115
+ onError: (error) => {
116
+ spinner.fail('Watch error');
117
+ this.error(error);
118
+ },
119
+ onWarn: (message) => {
120
+ this.warn(message);
121
+ },
122
+ onLog: (message) => {
123
+ if (flags.verbose) {
124
+ this.log(message);
125
+ }
126
+ }
127
+ });
128
+ // Keep process alive
129
+ await new Promise(() => {
130
+ // This promise never resolves, keeping the process running
131
+ // until interrupted by SIGINT/SIGTERM
132
+ });
133
+ }
134
+ catch (error) {
135
+ spinner.fail('Watch failed');
136
+ // Enhanced error logging
137
+ this.log('\n=== Watch Error Details ===');
138
+ this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
139
+ this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
140
+ if (error instanceof Error && error.stack) {
141
+ this.log(`\nStack trace:`);
142
+ this.log(error.stack);
143
+ }
144
+ this.error(error);
145
+ }
146
+ }
147
+ }
148
+ exports.default = Watch;
@@ -0,0 +1,7 @@
1
+ import { Hook } from '@oclif/core';
2
+ /**
3
+ * Init hook runs once when the CLI starts up.
4
+ * Currently unused but required by oclif configuration.
5
+ */
6
+ declare const hook: Hook<'init'>;
7
+ export default hook;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Init hook runs once when the CLI starts up.
5
+ * Currently unused but required by oclif configuration.
6
+ */
7
+ const hook = async function () {
8
+ // No initialization needed at this time
9
+ };
10
+ exports.default = hook;