@memberjunction/cli 5.41.0 → 5.42.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 (104) hide show
  1. package/dist/commands/bundle/index.d.ts +1 -0
  2. package/dist/commands/bundle/index.d.ts.map +1 -1
  3. package/dist/commands/bundle/index.js +7 -1
  4. package/dist/commands/bundle/index.js.map +1 -1
  5. package/dist/commands/codegen/index.d.ts +6 -16
  6. package/dist/commands/codegen/index.d.ts.map +1 -1
  7. package/dist/commands/codegen/index.js +6 -90
  8. package/dist/commands/codegen/index.js.map +1 -1
  9. package/dist/commands/codegen/usage.d.ts +7 -0
  10. package/dist/commands/codegen/usage.d.ts.map +1 -0
  11. package/dist/commands/codegen/usage.js +10 -0
  12. package/dist/commands/codegen/usage.js.map +1 -0
  13. package/dist/commands/install/claude.d.ts +35 -0
  14. package/dist/commands/install/claude.d.ts.map +1 -0
  15. package/dist/commands/install/claude.js +108 -0
  16. package/dist/commands/install/claude.js.map +1 -0
  17. package/dist/commands/install/index.d.ts +1 -0
  18. package/dist/commands/install/index.d.ts.map +1 -1
  19. package/dist/commands/install/index.js +4 -0
  20. package/dist/commands/install/index.js.map +1 -1
  21. package/dist/commands/migrate/convert.d.ts +59 -12
  22. package/dist/commands/migrate/convert.d.ts.map +1 -1
  23. package/dist/commands/migrate/convert.js +294 -21
  24. package/dist/commands/migrate/convert.js.map +1 -1
  25. package/dist/commands/migrate/rebake.d.ts +41 -0
  26. package/dist/commands/migrate/rebake.d.ts.map +1 -0
  27. package/dist/commands/migrate/rebake.js +193 -0
  28. package/dist/commands/migrate/rebake.js.map +1 -0
  29. package/dist/commands/plugin/add.d.ts +18 -0
  30. package/dist/commands/plugin/add.d.ts.map +1 -0
  31. package/dist/commands/plugin/add.js +53 -0
  32. package/dist/commands/plugin/add.js.map +1 -0
  33. package/dist/commands/sync/pull.d.ts +6 -26
  34. package/dist/commands/sync/pull.d.ts.map +1 -1
  35. package/dist/commands/sync/pull.js +6 -281
  36. package/dist/commands/sync/pull.js.map +1 -1
  37. package/dist/commands/sync/push.d.ts +8 -19
  38. package/dist/commands/sync/push.d.ts.map +1 -1
  39. package/dist/commands/sync/push.js +8 -328
  40. package/dist/commands/sync/push.js.map +1 -1
  41. package/dist/commands/sync/usage.d.ts +7 -0
  42. package/dist/commands/sync/usage.d.ts.map +1 -0
  43. package/dist/commands/sync/usage.js +10 -0
  44. package/dist/commands/sync/usage.js.map +1 -0
  45. package/dist/commands/update/claude.d.ts +37 -0
  46. package/dist/commands/update/claude.d.ts.map +1 -0
  47. package/dist/commands/update/claude.js +90 -0
  48. package/dist/commands/update/claude.js.map +1 -0
  49. package/dist/commands/usage.d.ts +17 -0
  50. package/dist/commands/usage.d.ts.map +1 -0
  51. package/dist/commands/usage.js +29 -0
  52. package/dist/commands/usage.js.map +1 -0
  53. package/dist/hooks/prerun.d.ts.map +1 -1
  54. package/dist/hooks/prerun.js +49 -8
  55. package/dist/hooks/prerun.js.map +1 -1
  56. package/dist/lib/claude-pack/ManagedBlockEditor.d.ts +40 -0
  57. package/dist/lib/claude-pack/ManagedBlockEditor.d.ts.map +1 -0
  58. package/dist/lib/claude-pack/ManagedBlockEditor.js +127 -0
  59. package/dist/lib/claude-pack/ManagedBlockEditor.js.map +1 -0
  60. package/dist/lib/claude-pack/PackFetcher.d.ts +86 -0
  61. package/dist/lib/claude-pack/PackFetcher.d.ts.map +1 -0
  62. package/dist/lib/claude-pack/PackFetcher.js +163 -0
  63. package/dist/lib/claude-pack/PackFetcher.js.map +1 -0
  64. package/dist/lib/claude-pack/PackInstaller.d.ts +62 -0
  65. package/dist/lib/claude-pack/PackInstaller.d.ts.map +1 -0
  66. package/dist/lib/claude-pack/PackInstaller.js +197 -0
  67. package/dist/lib/claude-pack/PackInstaller.js.map +1 -0
  68. package/dist/lib/claude-pack/PackMerger.d.ts +61 -0
  69. package/dist/lib/claude-pack/PackMerger.d.ts.map +1 -0
  70. package/dist/lib/claude-pack/PackMerger.js +315 -0
  71. package/dist/lib/claude-pack/PackMerger.js.map +1 -0
  72. package/dist/lib/claude-pack/PackOutputFormatter.d.ts +29 -0
  73. package/dist/lib/claude-pack/PackOutputFormatter.d.ts.map +1 -0
  74. package/dist/lib/claude-pack/PackOutputFormatter.js +82 -0
  75. package/dist/lib/claude-pack/PackOutputFormatter.js.map +1 -0
  76. package/dist/lib/claude-pack/PackPaths.d.ts +80 -0
  77. package/dist/lib/claude-pack/PackPaths.d.ts.map +1 -0
  78. package/dist/lib/claude-pack/PackPaths.js +182 -0
  79. package/dist/lib/claude-pack/PackPaths.js.map +1 -0
  80. package/dist/lib/claude-pack/PackTypes.d.ts +124 -0
  81. package/dist/lib/claude-pack/PackTypes.d.ts.map +1 -0
  82. package/dist/lib/claude-pack/PackTypes.js +32 -0
  83. package/dist/lib/claude-pack/PackTypes.js.map +1 -0
  84. package/dist/lib/claude-pack/SettingsMerger.d.ts +47 -0
  85. package/dist/lib/claude-pack/SettingsMerger.d.ts.map +1 -0
  86. package/dist/lib/claude-pack/SettingsMerger.js +163 -0
  87. package/dist/lib/claude-pack/SettingsMerger.js.map +1 -0
  88. package/dist/lib/cli-plugins.d.ts +16 -0
  89. package/dist/lib/cli-plugins.d.ts.map +1 -0
  90. package/dist/lib/cli-plugins.js +26 -0
  91. package/dist/lib/cli-plugins.js.map +1 -0
  92. package/dist/lib/domain-usage-command.d.ts +16 -0
  93. package/dist/lib/domain-usage-command.d.ts.map +1 -0
  94. package/dist/lib/domain-usage-command.js +23 -0
  95. package/dist/lib/domain-usage-command.js.map +1 -0
  96. package/dist/lib/usage-render.d.ts +14 -0
  97. package/dist/lib/usage-render.d.ts.map +1 -0
  98. package/dist/lib/usage-render.js +64 -0
  99. package/dist/lib/usage-render.js.map +1 -0
  100. package/dist/light-commands.d.ts.map +1 -1
  101. package/dist/light-commands.js +11 -0
  102. package/dist/light-commands.js.map +1 -1
  103. package/oclif.manifest.json +815 -135
  104. package/package.json +17 -15
@@ -0,0 +1,193 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { IncrementalBaker } from '@memberjunction/sql-converter';
6
+ import { MJPostgresTranspiler } from '@memberjunction/sqlglot-ts';
7
+ /**
8
+ * Re-bake the post-baseline PG migration set with INLINE NATIVE CodeGen (Path C), so the set
9
+ * deploys with `mj migrate` alone — no `mj codegen` step.
10
+ *
11
+ * Unlike `convert --bake-codegen` (which bakes UNCONVERTED migrations forward), `rebake` re-bakes
12
+ * migrations that already have a committed `.pg.sql`: it advances a live working DB by applying
13
+ * each migration's known-good committed file in order (which keeps dependent views consistent and
14
+ * registers new entities), then captures native CodeGen read-only and writes the baked file. A
15
+ * migration with a transpile gap (unhandled statement / hand-procedural) keeps its committed file.
16
+ *
17
+ * Requires a LIVE working PG database (DB_PLATFORM=postgresql + PG_* env, as `mj codegen`) seeded
18
+ * to the latest baseline; the command brings it forward as it re-bakes.
19
+ */
20
+ export default class MigrateRebake extends Command {
21
+ static { this.description = 'Re-bake the post-baseline PG migration set with inline native CodeGen'; }
22
+ static { this.examples = [
23
+ '<%= config.bin %> migrate rebake',
24
+ '<%= config.bin %> migrate rebake --baseline-floor V202605291452 --dry-run',
25
+ ]; }
26
+ static { this.flags = {
27
+ 'source-dir': Flags.string({ description: 'Source T-SQL migrations directory', default: './migrations/v5' }),
28
+ 'output-dir': Flags.string({ description: 'PG migrations directory (committed files read here + baked output written here)', default: './migrations-pg/v5' }),
29
+ schema: Flags.string({ description: 'Target schema name', default: '__mj' }),
30
+ 'baseline-floor': Flags.string({ description: 'Re-bake migrations strictly AFTER this version key (default: the latest B* in output-dir)' }),
31
+ 'dry-run': Flags.boolean({ description: 'Report what would be re-baked without writing files', default: false }),
32
+ }; }
33
+ async run() {
34
+ const { flags } = await this.parse(MigrateRebake);
35
+ const sourceDir = path.resolve(flags['source-dir']);
36
+ const outputDir = path.resolve(flags['output-dir']);
37
+ if (!fs.existsSync(sourceDir))
38
+ this.error(`Source directory not found: ${sourceDir}`);
39
+ if (!fs.existsSync(outputDir))
40
+ this.error(`Output directory not found: ${outputDir}`);
41
+ const floor = flags['baseline-floor'] ?? this.latestBaselineFloor(outputDir);
42
+ this.log(`Re-baking migrations after baseline floor: ${floor}`);
43
+ const migrations = this.postBaselineMigrations(sourceDir, floor);
44
+ if (migrations.length === 0) {
45
+ this.log('No post-baseline migrations to re-bake.');
46
+ return;
47
+ }
48
+ this.log(`${migrations.length} post-baseline migration(s) to re-bake.\n`);
49
+ const transpiler = await this.buildTranspiler(sourceDir);
50
+ const { baker, dispose } = await this.buildBaker(transpiler, flags.schema);
51
+ const summary = { baked: [], preserved: [], skipped: [] };
52
+ try {
53
+ for (const m of migrations) {
54
+ const ssSql = fs.readFileSync(path.join(sourceDir, m.source), 'utf8');
55
+ const committedPath = this.committedCounterpart(outputDir, m.outName, m.source);
56
+ if (!committedPath) {
57
+ this.warn(`${m.source}: no committed PG counterpart in output-dir — skipped (use convert --bake-codegen for new migrations).`);
58
+ summary.skipped.push(m.source);
59
+ continue;
60
+ }
61
+ // mj-sync metadata is owned by `mj sync push`, not CodeGen — preserve it verbatim.
62
+ if (/Metadata_Sync/i.test(m.source)) {
63
+ summary.preserved.push(m.source);
64
+ this.log(` kept ${m.source} (metadata-sync — committed preserved)`);
65
+ continue;
66
+ }
67
+ const committed = fs.readFileSync(committedPath, 'utf8');
68
+ try {
69
+ const result = await baker.bakeMigration(ssSql, m.source, committed);
70
+ if (result.mode === 'baked') {
71
+ if (!flags['dry-run'])
72
+ fs.writeFileSync(committedPath, result.pgSQL);
73
+ summary.baked.push(m.source);
74
+ this.log(` baked ${m.source} (E(M)=${result.affectedEntities.length})`);
75
+ }
76
+ else {
77
+ summary.preserved.push(m.source); // transpile gap — committed left in place
78
+ this.log(` kept ${m.source} (transpile gap — committed preserved)`);
79
+ }
80
+ }
81
+ catch (err) {
82
+ // Advancing or capturing this migration failed (e.g. a committed file that only applies
83
+ // under Skyway). Keep the committed file as-is and carry on so one migration can't abort
84
+ // the whole re-bake; the working DB simply lacks this migration's (non-schema) change.
85
+ summary.preserved.push(m.source);
86
+ this.warn(`${m.source}: re-bake failed (${err instanceof Error ? err.message : String(err)}) — committed preserved.`);
87
+ }
88
+ }
89
+ }
90
+ finally {
91
+ await dispose();
92
+ }
93
+ this.log('\n=== Re-bake Summary ===');
94
+ this.log(` baked native: ${summary.baked.length}`);
95
+ this.log(` preserved committed: ${summary.preserved.length}`);
96
+ this.log(` skipped (no PG): ${summary.skipped.length}`);
97
+ if (flags['dry-run'])
98
+ this.log(' (dry run — no files written)');
99
+ this.log('\nDeploy: mj migrate + mj sync push (no mj codegen step)');
100
+ // setupDataSource opened pg pools that keep the event loop alive; mirror `mj codegen`.
101
+ process.exit(0);
102
+ }
103
+ /** Highest `B<version>__...` baseline in the output dir → its version key is the floor. */
104
+ latestBaselineFloor(outputDir) {
105
+ const baselines = fs.readdirSync(outputDir).filter((f) => /^B\d.*\.pg\.sql$/.test(f)).sort();
106
+ if (baselines.length === 0)
107
+ this.error('No baseline (B*.pg.sql) found in output-dir; pass --baseline-floor.');
108
+ return baselines[baselines.length - 1].split('__')[0]; // e.g. "B202605291452" → compared lexically below
109
+ }
110
+ /** SS V-migrations whose version key sorts strictly after the floor (committed order). */
111
+ postBaselineMigrations(sourceDir, floor) {
112
+ const floorKey = floor.replace(/^B/, 'V'); // baseline keys start with B; migrations with V
113
+ return fs
114
+ .readdirSync(sourceDir)
115
+ .filter((f) => /^V.*\.sql$/.test(f) && !f.endsWith('.pg.sql') && !f.endsWith('.pg-only.sql'))
116
+ .filter((f) => f.split('__')[0] > floorKey)
117
+ .sort()
118
+ .map((source) => ({ source, outName: source.replace(/\.sql$/, '.pg.sql') }));
119
+ }
120
+ /** Committed PG counterpart: `.pg.sql` preferred, else a hand-authored `.pg-only.sql`. */
121
+ committedCounterpart(outputDir, outName, source) {
122
+ const pg = path.join(outputDir, outName);
123
+ if (fs.existsSync(pg))
124
+ return pg;
125
+ const pgOnly = path.join(outputDir, source.replace(/\.sql$/, '.pg-only.sql'));
126
+ return fs.existsSync(pgOnly) ? pgOnly : null;
127
+ }
128
+ /** AST transpiler with the cross-file BIT-column registry from the baselines (see convert.ts). */
129
+ async buildTranspiler(sourceDir) {
130
+ const probe = new MJPostgresTranspiler();
131
+ try {
132
+ await probe.preflight();
133
+ }
134
+ catch (err) {
135
+ this.error(err instanceof Error ? err.message : String(err));
136
+ }
137
+ const baselines = fs.readdirSync(sourceDir).filter((f) => /^B\d.*\.sql$/.test(f) && !f.endsWith('.pg.sql') && !f.endsWith('.pg-only.sql')).sort();
138
+ const bitColumns = [];
139
+ for (const b of baselines) {
140
+ try {
141
+ bitColumns.push(...(await probe.collectBitColumns(fs.readFileSync(path.join(sourceDir, b), 'utf8'))));
142
+ }
143
+ catch (err) {
144
+ this.warn(`Bit-column collection failed for ${b}: ${err instanceof Error ? err.message : String(err)}`);
145
+ }
146
+ }
147
+ return new MJPostgresTranspiler({ extraBitColumns: bitColumns });
148
+ }
149
+ /**
150
+ * Wire the working-DB capabilities to a live PG connection. The codegen engine is loaded with a
151
+ * dynamic import (Rule 8 case 3 — heavy module, opt-in path only); the matching `import type` is
152
+ * erased at runtime so it does not defeat the deferral.
153
+ */
154
+ async buildBaker(transpiler, schema) {
155
+ const { RunCodeGenBase, SQLCodeGenBase, initializeConfig, dbPlatform } = await import('@memberjunction/codegen-lib');
156
+ if (dbPlatform() !== 'postgresql') {
157
+ this.error('migrate rebake requires DB_PLATFORM=postgresql with PG_* connection env (the working DB CodeGen is captured from).');
158
+ }
159
+ initializeConfig(process.cwd());
160
+ let ds;
161
+ try {
162
+ ds = await new RunCodeGenBase().setupDataSource();
163
+ }
164
+ catch (err) {
165
+ this.error(`migrate rebake could not connect/load metadata from the working PG database: ${err instanceof Error ? err.message : String(err)}. ` +
166
+ 'Seed the working DB to the latest baseline before re-baking.');
167
+ }
168
+ this.log(` working DB: ${ds.connectionInfo}\n`);
169
+ const sqlGen = new SQLCodeGenBase();
170
+ const db = {
171
+ // Committed files may still carry the `${flyway:defaultSchema}` macro (Skyway substitutes it
172
+ // at deploy); raw pg.query can't, so substitute before applying to advance the working DB.
173
+ apply: async (sql) => { await ds.connection.query(sql.replaceAll('${flyway:defaultSchema}', schema)); },
174
+ refreshMetadata: async () => { await ds.provider.Refresh(); },
175
+ captureEntity: async (name) => {
176
+ const entity = ds.provider.EntityByName(name);
177
+ if (!entity)
178
+ throw new Error(`migrate rebake: entity not found in working-DB metadata: ${name}`);
179
+ const r = await sqlGen.generateSingleEntitySQLToSeparateFiles({
180
+ pool: ds.connection,
181
+ entity,
182
+ directory: os.tmpdir(),
183
+ onlyPermissions: false,
184
+ writeFiles: false,
185
+ skipExecution: true, // READ-ONLY: capture only; the committed file advances the working DB
186
+ });
187
+ return { sql: r.sql ?? '', permissionsSQL: r.permissionsSQL ?? '' };
188
+ },
189
+ };
190
+ return { baker: new IncrementalBaker({ transpiler, db, schema }), dispose: async () => { } };
191
+ }
192
+ }
193
+ //# sourceMappingURL=rebake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebake.js","sourceRoot":"","sources":["../../../src/commands/migrate/rebake.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { Command } from '@oclif/core';
2
+ /**
3
+ * Phase 3 convenience: register a third-party CLI plugin by appending its
4
+ * package specifier to `mj-cli-plugins.json` (created if absent). No MJCLI
5
+ * changes needed to extend the CLI — add a package, register it here (plan D9).
6
+ */
7
+ export default class PluginAdd extends Command {
8
+ static description: string;
9
+ static examples: string[];
10
+ static args: {
11
+ package: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ static flags: {
14
+ format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
15
+ };
16
+ run(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/commands/plugin/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,EAAS,MAAM,aAAa,CAAC;AAKnD;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO;IAC5C,MAAM,CAAC,WAAW,SAAuF;IAEzG,MAAM,CAAC,QAAQ,WAGb;IAEF,MAAM,CAAC,IAAI;;MAET;IAEF,MAAM,CAAC,KAAK;;MAEV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC3B"}
@@ -0,0 +1,53 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { PLUGIN_CONFIG_FILENAME } from '@memberjunction/cli-core';
3
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
4
+ import { resolve } from 'path';
5
+ /**
6
+ * Phase 3 convenience: register a third-party CLI plugin by appending its
7
+ * package specifier to `mj-cli-plugins.json` (created if absent). No MJCLI
8
+ * changes needed to extend the CLI — add a package, register it here (plan D9).
9
+ */
10
+ export default class PluginAdd extends Command {
11
+ static { this.description = 'Register a CLI plugin package in mj-cli-plugins.json so `mj` loads it at startup.'; }
12
+ static { this.examples = [
13
+ `<%= config.bin %> <%= command.id %> @my-org/mj-plugin/plugins`,
14
+ `<%= config.bin %> <%= command.id %> ./local-plugins/my-tool`,
15
+ ]; }
16
+ static { this.args = {
17
+ package: Args.string({ required: true, description: 'Plugin entry-point specifier (package name, subpath, or relative path)' }),
18
+ }; }
19
+ static { this.flags = {
20
+ format: Flags.string({ options: ['text', 'json'], default: 'text', description: 'Output format' }),
21
+ }; }
22
+ async run() {
23
+ const { args, flags } = await this.parse(PluginAdd);
24
+ const configPath = resolve(process.cwd(), PLUGIN_CONFIG_FILENAME);
25
+ // Preserve any other keys (e.g. "$comment") when rewriting.
26
+ const config = existsSync(configPath)
27
+ ? JSON.parse(readFileSync(configPath, 'utf-8'))
28
+ : {};
29
+ const plugins = Array.isArray(config.plugins) ? config.plugins : [];
30
+ const alreadyPresent = plugins.includes(args.package);
31
+ if (!alreadyPresent) {
32
+ plugins.push(args.package);
33
+ config.plugins = plugins;
34
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
35
+ }
36
+ const result = {
37
+ success: true,
38
+ command: 'plugin:add',
39
+ durationSeconds: 0,
40
+ data: { package: args.package, configPath, alreadyPresent, plugins },
41
+ };
42
+ if (flags.format === 'json') {
43
+ this.log(JSON.stringify(result, null, 2));
44
+ }
45
+ else if (alreadyPresent) {
46
+ this.log(`Plugin "${args.package}" is already registered in ${PLUGIN_CONFIG_FILENAME}.`);
47
+ }
48
+ else {
49
+ this.log(`Registered plugin "${args.package}" in ${PLUGIN_CONFIG_FILENAME}.`);
50
+ }
51
+ }
52
+ }
53
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/commands/plugin/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO;aACrC,gBAAW,GAAG,mFAAmF,CAAC;aAElG,aAAQ,GAAG;QAChB,+DAA+D;QAC/D,6DAA6D;KAC9D,CAAC;aAEK,SAAI,GAAG;QACZ,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wEAAwE,EAAE,CAAC;KAChI,CAAC;aAEK,UAAK,GAAG;QACb,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;KACnG,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,CAAC;QAElE,4DAA4D;QAC5D,MAAM,MAAM,GAA4B,UAAU,CAAC,UAAU,CAAC;YAC5D,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAA6B;YAC5E,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,OAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;QAElF,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;YACzB,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,MAAM,GAAG;YACb,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,YAAY;YACrB,eAAe,EAAE,CAAC;YAClB,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE;SACrE,CAAC;QAEF,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,cAAc,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,OAAO,8BAA8B,sBAAsB,GAAG,CAAC,CAAC;QAC3F,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,OAAO,QAAQ,sBAAsB,GAAG,CAAC,CAAC;QAChF,CAAC;IACH,CAAC"}
@@ -1,27 +1,7 @@
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, 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, 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
- 'update-existing': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
- 'create-new': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
- 'backup-before-update': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
- 'merge-strategy': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
16
- 'backup-directory': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
17
- 'preserve-fields': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
18
- 'exclude-fields': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
19
- 'target-dir': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
20
- incremental: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
- since: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
22
- };
23
- run(): Promise<void>;
24
- private findEntityDirectories;
25
- private findAllEntityDirectoriesRecursive;
26
- }
1
+ /**
2
+ * Thin oclif shim the real logic lives in {@link SyncPullPlugin} in
3
+ * `@memberjunction/metadata-sync/plugins` (plan §3). See sync/push.ts for the
4
+ * shim rationale.
5
+ */
6
+ export { SyncPullPlugin as default } from '@memberjunction/metadata-sync/plugins';
27
7
  //# sourceMappingURL=pull.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAO7C,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,OAAO;IACvC,MAAM,CAAC,WAAW,SAAgD;IAElE,MAAM,CAAC,QAAQ,WAOb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;;MA0BV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YA8MZ,qBAAqB;IA+BnC,OAAO,CAAC,iCAAiC;CAwB1C"}
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/pull.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,cAAc,IAAI,OAAO,EAAE,MAAM,uCAAuC,CAAC"}
@@ -1,282 +1,7 @@
1
- import { Command, Flags } from '@oclif/core';
2
- import { select } from '@inquirer/prompts';
3
- import ora from 'ora-classic';
4
- import chalk from 'chalk';
5
- import fs from 'fs';
6
- import path from 'path';
7
- export default class Pull extends Command {
8
- static { this.description = 'Pull metadata from database to local files'; }
9
- static { this.examples = [
10
- `<%= config.bin %> <%= command.id %> --entity="MJ: AI Prompts"`,
11
- `<%= config.bin %> <%= command.id %> --entity="MJ: AI Prompts" --filter="CategoryID='customer-service-id'"`,
12
- `<%= config.bin %> <%= command.id %> --entity="MJ: AI Agents" --merge-strategy=overwrite`,
13
- `<%= config.bin %> <%= command.id %> --entity="Actions" --target-dir=./custom-actions --no-validate`,
14
- `<%= config.bin %> <%= command.id %> --entity="Templates" --dry-run --verbose`,
15
- `<%= config.bin %> <%= command.id %> --entity="MJ: AI Prompts" --exclude-fields=InternalNotes,DebugInfo`,
16
- ]; }
17
- static { this.flags = {
18
- entity: Flags.string({ description: 'Entity name to pull', required: true }),
19
- filter: Flags.string({ description: 'Additional filter for pulling specific records' }),
20
- 'dry-run': Flags.boolean({ description: 'Show what would be pulled without actually pulling' }),
21
- 'multi-file': Flags.string({ description: 'Create a single file with multiple records (provide filename)' }),
22
- verbose: Flags.boolean({ char: 'v', description: 'Show detailed output' }),
23
- 'no-validate': Flags.boolean({ description: 'Skip validation before pull' }),
24
- 'update-existing': Flags.boolean({ description: 'Update existing records during pull', default: true }),
25
- 'create-new': Flags.boolean({ description: 'Create new files for records not found locally', default: true }),
26
- 'backup-before-update': Flags.boolean({ description: 'Create backups before updating files', default: true }),
27
- 'merge-strategy': Flags.string({
28
- description: 'Merge strategy for updates',
29
- options: ['merge', 'overwrite', 'skip'],
30
- default: 'merge'
31
- }),
32
- 'backup-directory': Flags.string({ description: 'Custom backup directory (default: .backups)' }),
33
- 'preserve-fields': Flags.string({ description: 'Comma-separated list of fields to preserve during updates', multiple: true }),
34
- 'exclude-fields': Flags.string({ description: 'Comma-separated list of fields to exclude from pull', multiple: true }),
35
- 'target-dir': Flags.string({ description: 'Specific target directory (overrides auto-discovery)' }),
36
- incremental: Flags.boolean({
37
- description: 'Only pull records updated since last successful pull',
38
- default: false
39
- }),
40
- since: Flags.string({
41
- description: 'Only pull records updated after this ISO 8601 timestamp (e.g., 2026-04-07T10:00:00Z)'
42
- }),
43
- }; }
44
- async run() {
45
- const { PullService, ValidationService, FormattingService, loadMJConfig, initializeProvider, getSyncEngine, getSystemUser, resetSyncEngine, configManager, loadEntityConfig, FileBackupManager, } = await import('@memberjunction/metadata-sync');
46
- const { flags } = await this.parse(Pull);
47
- const spinner = ora();
48
- let backupManager = null;
49
- try {
50
- // Load MJ config first
51
- spinner.start('Loading configuration');
52
- const mjConfig = loadMJConfig();
53
- if (!mjConfig) {
54
- this.error('No mj.config.cjs found in current directory or parent directories');
55
- }
56
- // Stop spinner before provider initialization
57
- spinner.stop();
58
- // Initialize data provider
59
- await initializeProvider(mjConfig);
60
- // Get singleton sync engine
61
- const syncEngine = await getSyncEngine(getSystemUser());
62
- // Show success after initialization
63
- if (flags.verbose) {
64
- spinner.succeed('Configuration and metadata loaded');
65
- }
66
- else {
67
- spinner.stop();
68
- }
69
- // Find entity directories
70
- const entityDirectories = await this.findEntityDirectories(flags.entity, configManager, loadEntityConfig);
71
- if (entityDirectories.length === 0) {
72
- this.error(`No directories found for entity "${flags.entity}". Make sure the entity configuration exists in a .mj-sync.json file.`);
73
- }
74
- // Select target directory
75
- let targetDir = flags['target-dir'];
76
- if (!targetDir) {
77
- if (entityDirectories.length === 1) {
78
- targetDir = entityDirectories[0];
79
- }
80
- else {
81
- targetDir = await select({
82
- message: `Multiple directories found for entity "${flags.entity}". Which one to use?`,
83
- choices: entityDirectories.map(dir => ({ name: dir, value: dir }))
84
- });
85
- }
86
- }
87
- // Run validation on target directory unless disabled
88
- if (!flags['no-validate']) {
89
- spinner.start('Validating target directory...');
90
- const validator = new ValidationService({ verbose: flags.verbose });
91
- const formatter = new FormattingService();
92
- const validationResult = await validator.validateDirectory(targetDir);
93
- spinner.stop();
94
- if (!validationResult.isValid || validationResult.warnings.length > 0) {
95
- // Show validation results
96
- this.log('\n' + formatter.formatValidationResult(validationResult, flags.verbose));
97
- if (!validationResult.isValid) {
98
- // Ask for confirmation
99
- const shouldContinue = await select({
100
- message: 'Validation failed with errors. Do you want to continue anyway?',
101
- choices: [
102
- { name: 'No, fix the errors first', value: false },
103
- { name: 'Yes, continue anyway', value: true }
104
- ],
105
- default: false
106
- });
107
- if (!shouldContinue) {
108
- this.error('Pull cancelled due to validation errors.');
109
- }
110
- }
111
- }
112
- else {
113
- this.log(chalk.green('✓ Validation passed'));
114
- }
115
- }
116
- // Initialize backup manager if backups are enabled
117
- if (flags['backup-before-update']) {
118
- backupManager = new FileBackupManager();
119
- await backupManager.initialize();
120
- }
121
- // Create pull service with optional state manager for incremental sync
122
- const pullService = new PullService(syncEngine, getSystemUser());
123
- if (flags.incremental || flags.since) {
124
- const { SyncStateManager } = await import('@memberjunction/metadata-sync');
125
- const stateManager = new SyncStateManager(targetDir);
126
- await stateManager.load();
127
- pullService.setStateManager(stateManager);
128
- }
129
- // Build pull options - only include CLI flags that were explicitly provided
130
- const pullOptions = {
131
- entity: flags.entity,
132
- targetDir,
133
- dryRun: flags['dry-run'],
134
- verbose: flags.verbose,
135
- noValidate: flags['no-validate']
136
- };
137
- // Add optional flags only if explicitly provided to avoid overriding entity config
138
- if (flags.filter !== undefined)
139
- pullOptions.filter = flags.filter;
140
- if (flags['multi-file'] !== undefined)
141
- pullOptions.multiFile = flags['multi-file'];
142
- if (flags['update-existing'] !== undefined)
143
- pullOptions.updateExistingRecords = flags['update-existing'];
144
- if (flags['create-new'] !== undefined)
145
- pullOptions.createNewFileIfNotFound = flags['create-new'];
146
- if (flags['backup-before-update'] !== undefined)
147
- pullOptions.backupBeforeUpdate = flags['backup-before-update'];
148
- if (flags['merge-strategy'] !== undefined)
149
- pullOptions.mergeStrategy = flags['merge-strategy'];
150
- if (flags['backup-directory'] !== undefined)
151
- pullOptions.backupDirectory = flags['backup-directory'];
152
- if (flags['preserve-fields'] !== undefined)
153
- pullOptions.preserveFields = flags['preserve-fields'];
154
- if (flags['exclude-fields'] !== undefined)
155
- pullOptions.excludeFields = flags['exclude-fields'];
156
- if (flags.incremental)
157
- pullOptions.incremental = flags.incremental;
158
- if (flags.since !== undefined)
159
- pullOptions.since = flags.since;
160
- await pullService.pull(pullOptions, {
161
- onProgress: (message) => {
162
- spinner.start(message);
163
- },
164
- onSuccess: (message) => {
165
- spinner.succeed(message);
166
- },
167
- onError: (message) => {
168
- spinner.fail(message);
169
- },
170
- onWarn: (message) => {
171
- this.warn(message);
172
- },
173
- onLog: (message) => {
174
- this.log(message);
175
- }
176
- });
177
- // Clean up backups on success
178
- if (backupManager && !flags['dry-run']) {
179
- await backupManager.cleanup();
180
- if (flags.verbose) {
181
- this.log(chalk.green('✓ Temporary backup files cleaned up'));
182
- }
183
- }
184
- // Clean up persistent backup files created by PullService
185
- if (!flags['dry-run']) {
186
- try {
187
- const backupCount = pullService.getCreatedBackupFiles().length;
188
- await pullService.cleanupBackupFiles();
189
- if (flags.verbose && backupCount > 0) {
190
- this.log(chalk.green(`✓ Cleaned up ${backupCount} persistent backup files`));
191
- }
192
- }
193
- catch (cleanupError) {
194
- this.warn(`Failed to cleanup persistent backup files: ${cleanupError}`);
195
- }
196
- }
197
- if (!flags['dry-run']) {
198
- this.log(`\n✅ Pull completed successfully`);
199
- }
200
- }
201
- catch (error) {
202
- spinner.fail('Pull failed');
203
- // Rollback backups on error
204
- if (backupManager) {
205
- try {
206
- await backupManager.rollback();
207
- if (flags.verbose) {
208
- this.log(chalk.yellow('↩️ Backup files restored'));
209
- }
210
- }
211
- catch (rollbackError) {
212
- this.warn(`Failed to rollback backup files: ${rollbackError}`);
213
- }
214
- }
215
- // Enhanced error logging
216
- this.log('\n=== Pull Error Details ===');
217
- this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
218
- this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
219
- if (error instanceof Error && error.stack) {
220
- this.log(`\nStack trace:`);
221
- this.log(error.stack);
222
- }
223
- // Context information
224
- this.log(`\nContext:`);
225
- this.log(`- Working directory: ${process.cwd()}`);
226
- this.log(`- Entity: ${flags.entity || 'not specified'}`);
227
- this.log(`- Filter: ${flags.filter || 'none'}`);
228
- this.error(error);
229
- }
230
- finally {
231
- // Reset singletons
232
- resetSyncEngine();
233
- }
234
- }
235
- async findEntityDirectories(entityName, configMgr, loadConfig) {
236
- try {
237
- const workingDir = configMgr.getOriginalCwd();
238
- // Search recursively for all directories with .mj-sync.json files
239
- const allDirs = this.findAllEntityDirectoriesRecursive(workingDir);
240
- // Filter directories that match the requested entity
241
- const entityDirs = [];
242
- for (const dir of allDirs) {
243
- try {
244
- const config = await loadConfig(dir);
245
- if (config && config.entity === entityName) {
246
- entityDirs.push(dir);
247
- }
248
- }
249
- catch (error) {
250
- this.warn(`Skipping directory ${dir}: invalid configuration (${error})`);
251
- }
252
- }
253
- return entityDirs;
254
- }
255
- catch (error) {
256
- this.error(`Failed to find entity directories: ${error}`);
257
- }
258
- }
259
- findAllEntityDirectoriesRecursive(dir) {
260
- const directories = [];
261
- try {
262
- // Check if current directory has .mj-sync.json
263
- const syncConfigPath = path.join(dir, '.mj-sync.json');
264
- if (fs.existsSync(syncConfigPath)) {
265
- directories.push(dir);
266
- }
267
- // Recursively search subdirectories
268
- const items = fs.readdirSync(dir, { withFileTypes: true });
269
- for (const item of items) {
270
- if (item.isDirectory() && !item.name.startsWith('.')) {
271
- const subdirPath = path.join(dir, item.name);
272
- directories.push(...this.findAllEntityDirectoriesRecursive(subdirPath));
273
- }
274
- }
275
- }
276
- catch (error) {
277
- // Skip directories we can't read
278
- }
279
- return directories;
280
- }
281
- }
1
+ /**
2
+ * Thin oclif shim — the real logic lives in {@link SyncPullPlugin} in
3
+ * `@memberjunction/metadata-sync/plugins` (plan §3). See sync/push.ts for the
4
+ * shim rationale.
5
+ */
6
+ export { SyncPullPlugin as default } from '@memberjunction/metadata-sync/plugins';
282
7
  //# sourceMappingURL=pull.js.map