relq 1.0.2 → 1.0.4

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 (92) hide show
  1. package/dist/cjs/cli/commands/add.cjs +403 -27
  2. package/dist/cjs/cli/commands/branch.cjs +13 -23
  3. package/dist/cjs/cli/commands/checkout.cjs +16 -29
  4. package/dist/cjs/cli/commands/cherry-pick.cjs +3 -4
  5. package/dist/cjs/cli/commands/commit.cjs +21 -29
  6. package/dist/cjs/cli/commands/diff.cjs +28 -32
  7. package/dist/cjs/cli/commands/export.cjs +7 -7
  8. package/dist/cjs/cli/commands/fetch.cjs +15 -21
  9. package/dist/cjs/cli/commands/generate.cjs +28 -54
  10. package/dist/cjs/cli/commands/history.cjs +19 -40
  11. package/dist/cjs/cli/commands/import.cjs +34 -41
  12. package/dist/cjs/cli/commands/init.cjs +69 -59
  13. package/dist/cjs/cli/commands/introspect.cjs +4 -8
  14. package/dist/cjs/cli/commands/log.cjs +26 -32
  15. package/dist/cjs/cli/commands/merge.cjs +24 -41
  16. package/dist/cjs/cli/commands/migrate.cjs +12 -25
  17. package/dist/cjs/cli/commands/pull.cjs +216 -106
  18. package/dist/cjs/cli/commands/push.cjs +35 -75
  19. package/dist/cjs/cli/commands/remote.cjs +2 -1
  20. package/dist/cjs/cli/commands/reset.cjs +22 -43
  21. package/dist/cjs/cli/commands/resolve.cjs +12 -14
  22. package/dist/cjs/cli/commands/rollback.cjs +16 -38
  23. package/dist/cjs/cli/commands/stash.cjs +5 -7
  24. package/dist/cjs/cli/commands/status.cjs +5 -10
  25. package/dist/cjs/cli/commands/sync.cjs +30 -50
  26. package/dist/cjs/cli/commands/tag.cjs +3 -4
  27. package/dist/cjs/cli/index.cjs +72 -9
  28. package/dist/cjs/cli/utils/change-tracker.cjs +107 -3
  29. package/dist/cjs/cli/utils/cli-utils.cjs +217 -0
  30. package/dist/cjs/cli/utils/config-loader.cjs +34 -8
  31. package/dist/cjs/cli/utils/fast-introspect.cjs +109 -3
  32. package/dist/cjs/cli/utils/git-utils.cjs +42 -161
  33. package/dist/cjs/cli/utils/pool-manager.cjs +156 -0
  34. package/dist/cjs/cli/utils/project-root.cjs +56 -5
  35. package/dist/cjs/cli/utils/relqignore.cjs +1 -0
  36. package/dist/cjs/cli/utils/repo-manager.cjs +47 -0
  37. package/dist/cjs/cli/utils/schema-comparator.cjs +301 -11
  38. package/dist/cjs/cli/utils/schema-diff.cjs +202 -1
  39. package/dist/cjs/cli/utils/schema-hash.cjs +2 -1
  40. package/dist/cjs/cli/utils/schema-introspect.cjs +7 -3
  41. package/dist/cjs/cli/utils/snapshot-manager.cjs +1 -0
  42. package/dist/cjs/cli/utils/spinner.cjs +14 -106
  43. package/dist/cjs/cli/utils/sql-generator.cjs +10 -2
  44. package/dist/cjs/cli/utils/type-generator.cjs +28 -16
  45. package/dist/config.d.ts +16 -6
  46. package/dist/esm/cli/commands/add.js +372 -29
  47. package/dist/esm/cli/commands/branch.js +14 -24
  48. package/dist/esm/cli/commands/checkout.js +16 -29
  49. package/dist/esm/cli/commands/cherry-pick.js +3 -4
  50. package/dist/esm/cli/commands/commit.js +22 -30
  51. package/dist/esm/cli/commands/diff.js +6 -10
  52. package/dist/esm/cli/commands/export.js +8 -8
  53. package/dist/esm/cli/commands/fetch.js +14 -20
  54. package/dist/esm/cli/commands/generate.js +28 -54
  55. package/dist/esm/cli/commands/history.js +11 -32
  56. package/dist/esm/cli/commands/import.js +35 -42
  57. package/dist/esm/cli/commands/init.js +65 -55
  58. package/dist/esm/cli/commands/introspect.js +4 -8
  59. package/dist/esm/cli/commands/log.js +6 -12
  60. package/dist/esm/cli/commands/merge.js +20 -37
  61. package/dist/esm/cli/commands/migrate.js +12 -25
  62. package/dist/esm/cli/commands/pull.js +204 -94
  63. package/dist/esm/cli/commands/push.js +21 -61
  64. package/dist/esm/cli/commands/remote.js +2 -1
  65. package/dist/esm/cli/commands/reset.js +16 -37
  66. package/dist/esm/cli/commands/resolve.js +13 -15
  67. package/dist/esm/cli/commands/rollback.js +16 -38
  68. package/dist/esm/cli/commands/stash.js +6 -8
  69. package/dist/esm/cli/commands/status.js +6 -11
  70. package/dist/esm/cli/commands/sync.js +30 -50
  71. package/dist/esm/cli/commands/tag.js +3 -4
  72. package/dist/esm/cli/index.js +72 -9
  73. package/dist/esm/cli/utils/change-tracker.js +107 -3
  74. package/dist/esm/cli/utils/cli-utils.js +169 -0
  75. package/dist/esm/cli/utils/config-loader.js +34 -8
  76. package/dist/esm/cli/utils/fast-introspect.js +109 -3
  77. package/dist/esm/cli/utils/git-utils.js +2 -124
  78. package/dist/esm/cli/utils/pool-manager.js +114 -0
  79. package/dist/esm/cli/utils/project-root.js +55 -5
  80. package/dist/esm/cli/utils/relqignore.js +1 -0
  81. package/dist/esm/cli/utils/repo-manager.js +42 -0
  82. package/dist/esm/cli/utils/schema-comparator.js +301 -11
  83. package/dist/esm/cli/utils/schema-diff.js +202 -1
  84. package/dist/esm/cli/utils/schema-hash.js +2 -1
  85. package/dist/esm/cli/utils/schema-introspect.js +7 -3
  86. package/dist/esm/cli/utils/snapshot-manager.js +1 -0
  87. package/dist/esm/cli/utils/spinner.js +1 -101
  88. package/dist/esm/cli/utils/sql-generator.js +10 -2
  89. package/dist/esm/cli/utils/type-generator.js +28 -16
  90. package/dist/index.d.ts +25 -8
  91. package/dist/schema-builder.d.ts +18 -7
  92. package/package.json +1 -1
@@ -36,22 +36,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.pushCommand = pushCommand;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
- const readline = __importStar(require("readline"));
40
39
  const config_loader_1 = require("../utils/config-loader.cjs");
41
40
  const env_loader_1 = require("../utils/env-loader.cjs");
42
- const spinner_1 = require("../utils/spinner.cjs");
41
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
43
42
  const fast_introspect_1 = require("../utils/fast-introspect.cjs");
44
43
  const relqignore_1 = require("../utils/relqignore.cjs");
45
44
  const repo_manager_1 = require("../utils/repo-manager.cjs");
46
45
  async function pushCommand(context) {
47
46
  const { config, flags } = context;
48
47
  if (!config) {
49
- console.error('Error: No configuration found.');
50
- process.exit(1);
48
+ (0, cli_utils_1.fatal)('No configuration found', `Run ${cli_utils_1.colors.cyan('relq init')} to create one.`);
51
49
  }
52
- (0, config_loader_1.requireValidConfig)(config);
50
+ await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'push' });
53
51
  const connection = config.connection;
54
- const projectRoot = process.cwd();
52
+ const { projectRoot } = context;
55
53
  const force = flags['force'] === true;
56
54
  const dryRun = flags['dry-run'] === true;
57
55
  const applySQL = flags['apply'] === true;
@@ -62,23 +60,17 @@ async function pushCommand(context) {
62
60
  const includeViews = config.includeViews ?? false;
63
61
  console.log('');
64
62
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
65
- console.log(`${spinner_1.colors.red('fatal:')} not a relq repository`);
66
- console.log('');
67
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq init')} ${spinner_1.colors.muted('to initialize.')}`);
68
- return;
63
+ (0, cli_utils_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${cli_utils_1.colors.cyan('relq init')} to initialize.`);
69
64
  }
70
65
  const localHead = (0, repo_manager_1.getHead)(projectRoot);
71
66
  if (!localHead) {
72
- console.log(`${spinner_1.colors.red('error:')} no commits to push`);
73
- console.log('');
74
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq commit -m "message"')} ${spinner_1.colors.muted('to create a commit.')}`);
75
- return;
67
+ (0, cli_utils_1.fatal)('no commits to push', `Run ${cli_utils_1.colors.cyan('relq commit -m "message"')} to create a commit.`);
76
68
  }
77
- const spinner = (0, spinner_1.createSpinner)();
69
+ const spinner = (0, cli_utils_1.createSpinner)();
78
70
  try {
79
71
  spinner.start('Connecting to remote...');
80
72
  await (0, repo_manager_1.ensureRemoteTable)(connection);
81
- spinner.succeed(`Connected to ${spinner_1.colors.cyan((0, env_loader_1.getConnectionDescription)(connection))}`);
73
+ spinner.succeed(`Connected to ${cli_utils_1.colors.cyan((0, env_loader_1.getConnectionDescription)(connection))}`);
82
74
  spinner.start('Checking remote commits...');
83
75
  const remoteCommits = await (0, repo_manager_1.fetchRemoteCommits)(connection, 100);
84
76
  const remoteHead = remoteCommits.length > 0 ? remoteCommits[0].hash : null;
@@ -96,9 +88,8 @@ async function pushCommand(context) {
96
88
  spinner.succeed(`Found ${remoteDb.tables.length} tables in remote`);
97
89
  const localSnapshot = (0, repo_manager_1.loadSnapshot)(projectRoot);
98
90
  if (!localSnapshot) {
99
- console.log(`${spinner_1.colors.red('error:')} No local snapshot found`);
100
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq pull')} ${spinner_1.colors.muted('or')} ${spinner_1.colors.cyan('relq import')} ${spinner_1.colors.muted('first.')}`);
101
- return;
91
+ spinner.stop();
92
+ (0, cli_utils_1.fatal)('No local snapshot found', `Run ${cli_utils_1.colors.cyan('relq pull')} or ${cli_utils_1.colors.cyan('relq import')} first.`);
102
93
  }
103
94
  const ignorePatterns = (0, relqignore_1.loadRelqignore)(projectRoot);
104
95
  const analysis = analyzeSync(localSnapshot, remoteDb, ignorePatterns, { includeFunctions, includeTriggers, includeViews });
@@ -106,86 +97,69 @@ async function pushCommand(context) {
106
97
  const hasObjectsToDrop = analysis.objectsToDrop.length > 0;
107
98
  const hasSchemaDrift = analysis.schemaDrift.length > 0;
108
99
  if (hasRemoteAhead && !force) {
109
- console.log('');
110
- console.log(`${spinner_1.colors.red('error:')} Remote has ${remoteMissing.length} commit(s) you don't have locally`);
100
+ spinner.stop();
111
101
  console.log('');
112
102
  for (const commit of remoteMissing.slice(0, 3)) {
113
- console.log(` ${spinner_1.colors.yellow((0, repo_manager_1.shortHash)(commit.hash))} ${commit.message}`);
103
+ console.log(` ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(commit.hash))} ${commit.message}`);
114
104
  }
115
105
  if (remoteMissing.length > 3) {
116
- console.log(` ${spinner_1.colors.muted(`... and ${remoteMissing.length - 3} more`)}`);
106
+ console.log(` ${cli_utils_1.colors.muted(`... and ${remoteMissing.length - 3} more`)}`);
117
107
  }
118
- console.log('');
119
- console.log(`${spinner_1.colors.yellow('hint:')} Run ${spinner_1.colors.cyan('relq pull')} to integrate remote changes.`);
120
- console.log(`${spinner_1.colors.yellow('hint:')} Use ${spinner_1.colors.cyan('relq push --force')} to override (may cause data loss).`);
121
- console.log('');
122
- return;
108
+ (0, cli_utils_1.fatal)(`Remote has ${remoteMissing.length} commit(s) you don't have locally`, `Run ${cli_utils_1.colors.cyan('relq pull')} to integrate remote changes.\nUse ${cli_utils_1.colors.cyan('relq push --force')} to override (may cause data loss).`);
123
109
  }
124
110
  if (hasObjectsToDrop) {
125
111
  console.log('');
126
- console.log(`${spinner_1.colors.yellow('⚠')} Remote has ${analysis.objectsToDrop.length} object(s) not in your local schema:`);
112
+ (0, cli_utils_1.warning)(`Remote has ${analysis.objectsToDrop.length} object(s) not in your local schema:`);
127
113
  console.log('');
128
114
  for (const obj of analysis.objectsToDrop.slice(0, 10)) {
129
- console.log(` ${spinner_1.colors.red('DROP')} ${obj.type.toLowerCase()}: ${obj.name}`);
115
+ console.log(` ${cli_utils_1.colors.red('DROP')} ${obj.type.toLowerCase()}: ${obj.name}`);
130
116
  }
131
117
  if (analysis.objectsToDrop.length > 10) {
132
- console.log(` ${spinner_1.colors.muted(`... and ${analysis.objectsToDrop.length - 10} more`)}`);
118
+ console.log(` ${cli_utils_1.colors.muted(`... and ${analysis.objectsToDrop.length - 10} more`)}`);
133
119
  }
134
120
  console.log('');
135
121
  if (!force) {
136
- console.log(`${spinner_1.colors.red('error:')} Cannot push - remote has objects not in your history`);
137
- console.log('');
138
- console.log(`${spinner_1.colors.yellow('hint:')} Run ${spinner_1.colors.cyan('relq pull')} first to sync your local schema.`);
139
- console.log(`${spinner_1.colors.yellow('hint:')} Use ${spinner_1.colors.cyan('relq push --force')} to DROP these objects from remote.`);
140
- console.log('');
141
- return;
122
+ (0, cli_utils_1.fatal)('Cannot push - remote has objects not in your history', `Run ${cli_utils_1.colors.cyan('relq pull')} first to sync your local schema.\nUse ${cli_utils_1.colors.cyan('relq push --force')} to DROP these objects from remote.`);
142
123
  }
143
124
  const dependencyErrors = checkDropDependencies(analysis.objectsToDrop, remoteDb, ignorePatterns);
144
125
  if (dependencyErrors.length > 0) {
145
- console.log(`${spinner_1.colors.red('error:')} Cannot drop objects due to dependencies:`);
146
126
  console.log('');
147
127
  for (const err of dependencyErrors) {
148
- console.log(` ${spinner_1.colors.red('✗')} ${err}`);
128
+ console.log(` ${err}`);
149
129
  }
150
- console.log('');
151
- console.log(`${spinner_1.colors.muted('These objects are used by non-ignored objects in your schema.')}`);
152
- console.log(`${spinner_1.colors.muted('Either add them to .relqignore or import them with')} ${spinner_1.colors.cyan('relq pull')}`);
153
- console.log('');
154
- return;
130
+ (0, cli_utils_1.fatal)('Cannot drop objects due to dependencies', `These objects are used by non-ignored objects in your schema.\nEither add them to .relqignore or import them with ${cli_utils_1.colors.cyan('relq pull')}`);
155
131
  }
156
132
  if (!skipPrompt && !dryRun) {
157
- console.log(`${spinner_1.colors.red('⚠ WARNING: This will DROP data from your database!')}`);
133
+ (0, cli_utils_1.warning)('This will DROP data from your database!');
158
134
  console.log('');
159
- const confirmed = await askConfirmation(`Drop ${analysis.objectsToDrop.length} object(s) from remote? [y/N] `);
135
+ const confirmed = await (0, cli_utils_1.confirm)(`Drop ${analysis.objectsToDrop.length} object(s) from remote?`, false);
160
136
  if (!confirmed) {
161
- console.log(`${spinner_1.colors.muted('Cancelled.')}`);
162
- console.log('');
163
- return;
137
+ (0, cli_utils_1.fatal)('Operation cancelled by user');
164
138
  }
165
139
  }
166
140
  }
167
141
  if (toPush.length === 0 && !hasObjectsToDrop) {
168
- console.log(`${spinner_1.colors.green('✓')} Everything up-to-date`);
142
+ console.log('Everything up-to-date');
169
143
  console.log('');
170
144
  return;
171
145
  }
172
146
  console.log('');
173
- console.log(`Pushing to ${spinner_1.colors.cyan((0, env_loader_1.getConnectionDescription)(connection))}`);
147
+ console.log(`Pushing to ${cli_utils_1.colors.cyan((0, env_loader_1.getConnectionDescription)(connection))}`);
174
148
  console.log('');
175
149
  if (toPush.length > 0) {
176
- console.log(`${spinner_1.colors.cyan('Commits:')} ${toPush.length}`);
150
+ console.log(`${cli_utils_1.colors.cyan('Commits:')} ${toPush.length}`);
177
151
  for (const commit of toPush.slice(0, 5)) {
178
- console.log(` ${spinner_1.colors.yellow((0, repo_manager_1.shortHash)(commit.hash))} ${commit.message}`);
152
+ console.log(` ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(commit.hash))} ${commit.message}`);
179
153
  }
180
154
  if (toPush.length > 5) {
181
- console.log(` ${spinner_1.colors.muted(`... and ${toPush.length - 5} more`)}`);
155
+ console.log(` ${cli_utils_1.colors.muted(`... and ${toPush.length - 5} more`)}`);
182
156
  }
183
157
  console.log('');
184
158
  }
185
159
  if (dryRun) {
186
- console.log(`${spinner_1.colors.yellow('Dry run')} - no changes applied`);
160
+ console.log(`${cli_utils_1.colors.yellow('Dry run')} - no changes applied`);
187
161
  console.log('');
188
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq push --apply')} ${spinner_1.colors.muted('to execute.')}`);
162
+ console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq push --apply')} ${cli_utils_1.colors.muted('to execute.')}`);
189
163
  console.log('');
190
164
  return;
191
165
  }
@@ -252,21 +226,20 @@ async function pushCommand(context) {
252
226
  }
253
227
  const oldHash = remoteHead ? (0, repo_manager_1.shortHash)(remoteHead) : '(none)';
254
228
  const newHash = (0, repo_manager_1.shortHash)(localHead);
255
- console.log(` ${oldHash}..${newHash} ${spinner_1.colors.muted('main -> main')}`);
229
+ console.log(` ${oldHash}..${newHash} ${cli_utils_1.colors.muted('main -> main')}`);
256
230
  if (hasObjectsToDrop && force && applySQL) {
257
231
  console.log('');
258
- console.log(`${spinner_1.colors.yellow('⚠')} Dropped ${analysis.objectsToDrop.length} object(s) from remote`);
232
+ (0, cli_utils_1.warning)(`Dropped ${analysis.objectsToDrop.length} object(s) from remote`);
259
233
  }
260
234
  if (!applySQL) {
261
235
  console.log('');
262
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq push --apply')} ${spinner_1.colors.muted('to execute SQL.')}`);
236
+ console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq push --apply')} ${cli_utils_1.colors.muted('to execute SQL.')}`);
263
237
  }
264
238
  console.log('');
265
239
  }
266
- catch (error) {
240
+ catch (err) {
267
241
  spinner.fail('Push failed');
268
- console.error(spinner_1.colors.red(`Error: ${error instanceof Error ? error.message : error}`));
269
- process.exit(1);
242
+ (0, cli_utils_1.fatal)(err instanceof Error ? err.message : String(err));
270
243
  }
271
244
  }
272
245
  function analyzeSync(local, remote, patterns, options) {
@@ -365,17 +338,4 @@ function generateDropSQL(obj) {
365
338
  return '';
366
339
  }
367
340
  }
368
- function askConfirmation(question) {
369
- const rl = readline.createInterface({
370
- input: process.stdin,
371
- output: process.stdout,
372
- });
373
- return new Promise((resolve) => {
374
- rl.question(question, (answer) => {
375
- rl.close();
376
- const a = answer.trim().toLowerCase();
377
- resolve(a === 'y' || a === 'yes');
378
- });
379
- });
380
- }
381
341
  exports.default = pushCommand;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.remoteCommand = remoteCommand;
4
+ const spinner_1 = require("../utils/spinner.cjs");
4
5
  async function remoteCommand(context) {
5
6
  console.log('');
6
- console.log('⚠️ Remote tracking is coming soon.');
7
+ (0, spinner_1.warning)('Remote tracking is coming soon.');
7
8
  console.log('');
8
9
  console.log('This will allow you to:');
9
10
  console.log(' • Track multiple remote databases');
@@ -37,40 +37,26 @@ exports.resetCommand = resetCommand;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const readline = __importStar(require("readline"));
40
- const spinner_1 = require("../utils/spinner.cjs");
40
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
41
41
  const repo_manager_1 = require("../utils/repo-manager.cjs");
42
42
  async function resetCommand(context) {
43
- const { flags, args } = context;
44
- const projectRoot = process.cwd();
43
+ const { flags, args, projectRoot } = context;
45
44
  console.log('');
46
45
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
47
- console.log(`${spinner_1.colors.red('fatal:')} not a relq repository`);
48
- return;
46
+ (0, cli_utils_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${cli_utils_1.colors.cyan('relq init')} to initialize.`);
49
47
  }
50
48
  const hard = flags['hard'] === true;
51
49
  const soft = flags['soft'] === true;
52
50
  const target = args[0];
53
51
  if (!target) {
54
- console.log(`${spinner_1.colors.red('error:')} Please specify a target`);
55
- console.log('');
56
- console.log('Usage:');
57
- console.log(` ${spinner_1.colors.cyan('relq reset --hard HEAD~1')} Reset to previous commit`);
58
- console.log(` ${spinner_1.colors.cyan('relq reset --hard <hash>')} Reset to specific commit`);
59
- console.log('');
60
- return;
52
+ (0, cli_utils_1.fatal)('Please specify a target', `Usage:\n ${cli_utils_1.colors.cyan('relq reset --hard HEAD~1')} Reset to previous commit\n ${cli_utils_1.colors.cyan('relq reset --hard <hash>')} Reset to specific commit`);
61
53
  }
62
54
  if (!hard && !soft) {
63
- console.log(`${spinner_1.colors.red('error:')} Please specify --hard or --soft`);
64
- console.log('');
65
- console.log(` ${spinner_1.colors.cyan('--hard')} Discard all changes (DANGEROUS)`);
66
- console.log(` ${spinner_1.colors.cyan('--soft')} Keep changes unstaged`);
67
- console.log('');
68
- return;
55
+ (0, cli_utils_1.fatal)('Please specify --hard or --soft', ` ${cli_utils_1.colors.cyan('--hard')} Discard all changes (DANGEROUS)\n ${cli_utils_1.colors.cyan('--soft')} Keep changes unstaged`);
69
56
  }
70
57
  const currentHead = (0, repo_manager_1.getHead)(projectRoot);
71
58
  if (!currentHead) {
72
- console.log(`${spinner_1.colors.red('error:')} No commits yet`);
73
- return;
59
+ (0, cli_utils_1.fatal)('No commits yet', `Run ${cli_utils_1.colors.cyan('relq pull')} or ${cli_utils_1.colors.cyan('relq import')} first.`);
74
60
  }
75
61
  const allCommits = (0, repo_manager_1.getAllCommits)(projectRoot);
76
62
  let targetHash = null;
@@ -91,23 +77,17 @@ async function resetCommand(context) {
91
77
  }
92
78
  }
93
79
  if (!targetHash) {
94
- console.log(`${spinner_1.colors.red('error:')} Cannot find commit: ${target}`);
95
- console.log('');
96
- console.log('Available commits:');
97
- for (const c of allCommits.slice(0, 5)) {
98
- console.log(` ${spinner_1.colors.yellow((0, repo_manager_1.shortHash)(c.hash))} ${c.message}`);
99
- }
100
- return;
80
+ const availableList = allCommits.slice(0, 5).map(c => ` ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(c.hash))} ${c.message}`).join('\n');
81
+ (0, cli_utils_1.fatal)(`Cannot find commit: ${target}`, `Available commits:\n${availableList}`);
101
82
  }
102
83
  const targetCommit = (0, repo_manager_1.loadCommit)(targetHash, projectRoot);
103
84
  if (!targetCommit) {
104
- console.log(`${spinner_1.colors.red('error:')} Cannot load commit: ${targetHash}`);
105
- return;
85
+ (0, cli_utils_1.fatal)(`Cannot load commit: ${targetHash}`);
106
86
  }
107
87
  if (hard) {
108
- console.log(`${spinner_1.colors.red('⚠ WARNING: This will discard all local changes!')}`);
88
+ (0, cli_utils_1.warning)('This will discard all local changes!');
109
89
  console.log('');
110
- console.log(`Reset to: ${spinner_1.colors.yellow((0, repo_manager_1.shortHash)(targetHash))} ${targetCommit.message}`);
90
+ console.log(`Reset to: ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(targetHash))} ${targetCommit.message}`);
111
91
  console.log('');
112
92
  const rl = readline.createInterface({
113
93
  input: process.stdin,
@@ -120,24 +100,24 @@ async function resetCommand(context) {
120
100
  });
121
101
  });
122
102
  if (!confirmed) {
123
- console.log(`${spinner_1.colors.muted('Cancelled.')}`);
103
+ console.log(`${cli_utils_1.colors.muted('Cancelled.')}`);
124
104
  console.log('');
125
105
  return;
126
106
  }
127
107
  }
128
- const spinner = (0, spinner_1.createSpinner)();
108
+ const spinner = (0, cli_utils_1.createSpinner)();
129
109
  try {
130
110
  spinner.start(`Resetting to ${(0, repo_manager_1.shortHash)(targetHash)}...`);
131
111
  const commitPath = path.join(projectRoot, '.relq', 'commits', `${targetHash}.json`);
132
112
  if (!fs.existsSync(commitPath)) {
133
- spinner.fail('Cannot find commit data');
134
- return;
113
+ spinner.stop();
114
+ (0, cli_utils_1.fatal)('Cannot find commit data - repository may be corrupt');
135
115
  }
136
116
  const commitData = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
137
117
  const targetSnapshot = commitData.snapshot;
138
118
  if (!targetSnapshot) {
139
- spinner.fail('Commit has no snapshot data');
140
- return;
119
+ spinner.stop();
120
+ (0, cli_utils_1.fatal)('Commit has no snapshot data - repository may be corrupt');
141
121
  }
142
122
  if (hard) {
143
123
  (0, repo_manager_1.saveSnapshot)(targetSnapshot, projectRoot);
@@ -152,18 +132,17 @@ async function resetCommand(context) {
152
132
  if (fs.existsSync(mergeStatePath))
153
133
  fs.unlinkSync(mergeStatePath);
154
134
  }
155
- spinner.succeed(`Reset to ${spinner_1.colors.yellow((0, repo_manager_1.shortHash)(targetHash))}`);
135
+ spinner.succeed(`Reset to ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(targetHash))}`);
156
136
  console.log('');
157
- console.log(`HEAD is now at ${spinner_1.colors.yellow((0, repo_manager_1.shortHash)(targetHash))} ${targetCommit.message}`);
137
+ console.log(`HEAD is now at ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(targetHash))} ${targetCommit.message}`);
158
138
  console.log('');
159
139
  if (soft) {
160
- console.log(`${spinner_1.colors.muted('Changes are unstaged. Use')} ${spinner_1.colors.cyan('relq status')} ${spinner_1.colors.muted('to see.')}`);
140
+ console.log(`${cli_utils_1.colors.muted('Changes are unstaged. Use')} ${cli_utils_1.colors.cyan('relq status')} ${cli_utils_1.colors.muted('to see.')}`);
161
141
  }
162
142
  }
163
- catch (error) {
143
+ catch (err) {
164
144
  spinner.fail('Reset failed');
165
- console.error(spinner_1.colors.red(`Error: ${error instanceof Error ? error.message : error}`));
166
- process.exit(1);
145
+ (0, cli_utils_1.fatal)(err instanceof Error ? err.message : String(err));
167
146
  }
168
147
  }
169
148
  exports.default = resetCommand;
@@ -39,23 +39,21 @@ const path = __importStar(require("path"));
39
39
  const spinner_1 = require("../utils/spinner.cjs");
40
40
  const repo_manager_1 = require("../utils/repo-manager.cjs");
41
41
  async function resolveCommand(context) {
42
- const { flags, args } = context;
43
- const projectRoot = process.cwd();
42
+ const { flags, args, projectRoot } = context;
44
43
  console.log('');
45
44
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
46
- console.log(`${spinner_1.colors.red('fatal:')} not a relq repository`);
47
- return;
45
+ (0, spinner_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${spinner_1.colors.cyan('relq init')} to initialize.`);
48
46
  }
49
47
  const mergeStatePath = path.join(projectRoot, '.relq', 'MERGE_STATE');
50
48
  if (!fs.existsSync(mergeStatePath)) {
51
- console.log(`${spinner_1.colors.green('✓')} No conflicts to resolve`);
49
+ console.log('No conflicts to resolve');
52
50
  console.log('');
53
51
  return;
54
52
  }
55
53
  const mergeState = JSON.parse(fs.readFileSync(mergeStatePath, 'utf-8'));
56
54
  if (mergeState.conflicts.length === 0) {
57
55
  fs.unlinkSync(mergeStatePath);
58
- console.log(`${spinner_1.colors.green('✓')} All conflicts resolved`);
56
+ console.log('All conflicts resolved');
59
57
  console.log('');
60
58
  return;
61
59
  }
@@ -69,9 +67,9 @@ async function resolveCommand(context) {
69
67
  console.log('');
70
68
  (0, repo_manager_1.saveSnapshot)(mergeState.remoteSnapshot, projectRoot);
71
69
  fs.unlinkSync(mergeStatePath);
72
- console.log(`${spinner_1.colors.green('✓')} Applied remote versions for all conflicts`);
70
+ console.log('Applied remote versions for all conflicts');
73
71
  console.log('');
74
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq commit -m "Merge remote changes"')} ${spinner_1.colors.muted('to complete.')}`);
72
+ console.log(`hint: run 'relq commit -m "Merge remote changes"' to complete`);
75
73
  console.log('');
76
74
  return;
77
75
  }
@@ -79,9 +77,9 @@ async function resolveCommand(context) {
79
77
  console.log(`Resolving ${mergeState.conflicts.length} conflict(s) with --all-ours`);
80
78
  console.log('');
81
79
  fs.unlinkSync(mergeStatePath);
82
- console.log(`${spinner_1.colors.green('✓')} Kept local versions for all conflicts`);
80
+ console.log('Kept local versions for all conflicts');
83
81
  console.log('');
84
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq commit -m "Keep local changes"')} ${spinner_1.colors.muted('to complete.')}`);
82
+ console.log(`hint: run 'relq commit -m "Keep local changes"' to complete`);
85
83
  console.log('');
86
84
  return;
87
85
  }
@@ -111,17 +109,17 @@ async function resolveCommand(context) {
111
109
  mergeState.conflicts = mergeState.conflicts.filter(c => c !== conflict);
112
110
  if (mergeState.conflicts.length === 0) {
113
111
  fs.unlinkSync(mergeStatePath);
114
- console.log(`${spinner_1.colors.green('✓')} All conflicts resolved`);
112
+ console.log('All conflicts resolved');
115
113
  }
116
114
  else {
117
115
  fs.writeFileSync(mergeStatePath, JSON.stringify(mergeState, null, 2));
118
- console.log(`${spinner_1.colors.green('✓')} Resolved: ${conflict.objectType} ${objectName}`);
119
- console.log(`${spinner_1.colors.yellow(String(mergeState.conflicts.length))} conflict(s) remaining`);
116
+ console.log(`Resolved: ${conflict.objectType} ${objectName}`);
117
+ console.log(`${mergeState.conflicts.length} conflict(s) remaining`);
120
118
  }
121
119
  console.log('');
122
120
  return;
123
121
  }
124
- console.log(`${spinner_1.colors.yellow('⚠')} You have ${mergeState.conflicts.length} unresolved conflict(s):`);
122
+ (0, spinner_1.warning)(`You have ${mergeState.conflicts.length} unresolved conflict(s):`);
125
123
  console.log('');
126
124
  for (const conflict of mergeState.conflicts) {
127
125
  const name = conflict.parentName
@@ -36,18 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.rollbackCommand = rollbackCommand;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
- const readline = __importStar(require("readline"));
40
39
  const config_loader_1 = require("../utils/config-loader.cjs");
41
40
  const env_loader_1 = require("../utils/env-loader.cjs");
42
- const colors = {
43
- reset: '\x1b[0m',
44
- bold: '\x1b[1m',
45
- dim: '\x1b[2m',
46
- red: '\x1b[31m',
47
- green: '\x1b[32m',
48
- yellow: '\x1b[33m',
49
- cyan: '\x1b[36m',
50
- };
41
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
51
42
  function parseMigration(content) {
52
43
  const upMatch = content.match(/--\s*UP\s*\n([\s\S]*?)(?=--\s*DOWN|$)/i);
53
44
  const downMatch = content.match(/--\s*DOWN\s*\n([\s\S]*?)$/i);
@@ -56,32 +47,20 @@ function parseMigration(content) {
56
47
  down: downMatch?.[1]?.trim() || '',
57
48
  };
58
49
  }
59
- function askConfirm(question) {
60
- const rl = readline.createInterface({
61
- input: process.stdin,
62
- output: process.stdout,
63
- });
64
- return new Promise((resolve) => {
65
- rl.question(`${question} [y/N]: `, (answer) => {
66
- rl.close();
67
- resolve(answer.trim().toLowerCase() === 'y');
68
- });
69
- });
70
- }
71
50
  async function rollbackCommand(context) {
72
51
  const { config, args, flags } = context;
73
52
  if (!config) {
74
- console.error('Error: No configuration found.');
75
- process.exit(1);
53
+ (0, cli_utils_1.fatal)('No configuration found', `run ${cli_utils_1.colors.cyan('relq init')} to create a configuration file`);
54
+ return;
76
55
  }
77
- (0, config_loader_1.requireValidConfig)(config);
56
+ await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'rollback' });
78
57
  const connection = config.connection;
79
58
  const migrationsDir = config.migrations?.directory || './migrations';
80
59
  const tableName = config.migrations?.tableName || '_relq_migrations';
81
60
  const count = parseInt(args[0]) || 1;
82
61
  const dryRun = flags['dry-run'] === true;
83
62
  const force = flags['force'] === true;
84
- console.log(`${colors.bold}Rolling back ${count} migration(s)...${colors.reset}`);
63
+ console.log(`Rolling back ${count} migration(s)...`);
85
64
  console.log(` Connection: ${(0, env_loader_1.getConnectionDescription)(connection)}`);
86
65
  console.log('');
87
66
  try {
@@ -101,17 +80,17 @@ async function rollbackCommand(context) {
101
80
  LIMIT $1;
102
81
  `, [count]);
103
82
  if (result.rows.length === 0) {
104
- console.log(`${colors.yellow}No migrations to rollback.${colors.reset}`);
83
+ console.log('No migrations to rollback.');
105
84
  return;
106
85
  }
107
86
  const toRollback = result.rows.map(r => r.name);
108
- console.log(`${colors.bold}Migrations to rollback:${colors.reset}`);
87
+ console.log('Migrations to rollback:');
109
88
  for (const name of toRollback) {
110
- console.log(` ${colors.red}• ${name}${colors.reset}`);
89
+ console.log(` - ${name}`);
111
90
  }
112
91
  console.log('');
113
92
  if (!force && !dryRun) {
114
- const proceed = await askConfirm(`${colors.red}This will undo ${toRollback.length} migration(s). Continue?${colors.reset}`);
93
+ const proceed = await (0, cli_utils_1.confirm)(`${cli_utils_1.colors.red('This will undo ' + toRollback.length + ' migration(s). Continue?')}`, false);
115
94
  if (!proceed) {
116
95
  console.log('Cancelled.');
117
96
  return;
@@ -120,18 +99,18 @@ async function rollbackCommand(context) {
120
99
  for (const name of toRollback) {
121
100
  const filePath = path.join(migrationsDir, name);
122
101
  if (!fs.existsSync(filePath)) {
123
- console.log(`${colors.yellow}⚠️ Migration file not found: ${name}${colors.reset}`);
102
+ (0, cli_utils_1.warning)(`Migration file not found: ${name}`);
124
103
  continue;
125
104
  }
126
105
  const content = fs.readFileSync(filePath, 'utf-8');
127
106
  const { down } = parseMigration(content);
128
107
  if (!down) {
129
- console.log(`${colors.yellow}⚠️ No DOWN section in: ${name}${colors.reset}`);
108
+ (0, cli_utils_1.warning)(`No DOWN section in: ${name}`);
130
109
  continue;
131
110
  }
132
111
  if (dryRun) {
133
- console.log(`${colors.cyan}[dry-run] Would rollback: ${name}${colors.reset}`);
134
- console.log(`${colors.dim}${down}${colors.reset}`);
112
+ console.log(`[dry-run] Would rollback: ${name}`);
113
+ console.log(down);
135
114
  console.log('');
136
115
  }
137
116
  else {
@@ -142,7 +121,7 @@ async function rollbackCommand(context) {
142
121
  await client.query(down);
143
122
  await client.query(`DELETE FROM "${tableName}" WHERE name = $1`, [name]);
144
123
  await client.query('COMMIT');
145
- console.log(` ${colors.green}✓ Rolled back${colors.reset}`);
124
+ console.log(' Rolled back');
146
125
  }
147
126
  catch (error) {
148
127
  await client.query('ROLLBACK');
@@ -155,7 +134,7 @@ async function rollbackCommand(context) {
155
134
  }
156
135
  if (!dryRun) {
157
136
  console.log('');
158
- console.log(`${colors.green}✓ Rolled back ${toRollback.length} migration(s).${colors.reset}`);
137
+ console.log(`Rolled back ${toRollback.length} migration(s).`);
159
138
  }
160
139
  }
161
140
  finally {
@@ -163,7 +142,6 @@ async function rollbackCommand(context) {
163
142
  }
164
143
  }
165
144
  catch (error) {
166
- console.error('Error:', error instanceof Error ? error.message : error);
167
- process.exit(1);
145
+ (0, cli_utils_1.fatal)('Rollback failed', error instanceof Error ? error.message : String(error));
168
146
  }
169
147
  }
@@ -39,13 +39,11 @@ const path = __importStar(require("path"));
39
39
  const spinner_1 = require("../utils/spinner.cjs");
40
40
  const repo_manager_1 = require("../utils/repo-manager.cjs");
41
41
  async function stashCommand(context) {
42
- const { args, flags } = context;
43
- const projectRoot = process.cwd();
42
+ const { args, flags, projectRoot } = context;
44
43
  const subcommand = args[0] || 'push';
45
44
  console.log('');
46
45
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
47
- console.log(`${spinner_1.colors.red('fatal:')} not a relq repository`);
48
- return;
46
+ (0, spinner_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${spinner_1.colors.cyan('relq init')} to initialize.`);
49
47
  }
50
48
  const stashDir = path.join(projectRoot, '.relq', 'stash');
51
49
  switch (subcommand) {
@@ -92,7 +90,7 @@ async function stashPush(projectRoot, stashDir, message) {
92
90
  fs.writeFileSync(stagedPath, '[]');
93
91
  if (fs.existsSync(unstagedPath))
94
92
  fs.writeFileSync(unstagedPath, '[]');
95
- console.log(`${spinner_1.colors.green('✓')} Saved working directory`);
93
+ console.log('Saved working directory');
96
94
  console.log(` stash@{${stashIdx}}: ${message}`);
97
95
  console.log('');
98
96
  }
@@ -117,7 +115,7 @@ async function stashPop(projectRoot, stashDir) {
117
115
  fs.writeFileSync(unstagedPath, JSON.stringify(stash.unstaged, null, 2));
118
116
  }
119
117
  fs.unlinkSync(stashPath);
120
- console.log(`${spinner_1.colors.green('✓')} Applied stash and dropped`);
118
+ console.log('Applied stash and dropped');
121
119
  console.log(` ${stash.message}`);
122
120
  console.log('');
123
121
  }
@@ -148,7 +146,7 @@ async function stashDrop(stashDir) {
148
146
  return;
149
147
  }
150
148
  fs.unlinkSync(path.join(stashDir, stashFiles[0]));
151
- console.log(`${spinner_1.colors.green('✓')} Dropped stash`);
149
+ console.log('Dropped stash');
152
150
  console.log('');
153
151
  }
154
152
  exports.default = stashCommand;