relq 1.0.2 → 1.0.3

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 +1 -1
  44. package/dist/cjs/cli/utils/type-generator.cjs +28 -16
  45. package/dist/config.d.ts +16 -25
  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 +1 -1
  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 +16 -6
  92. package/package.json +1 -1
@@ -37,27 +37,19 @@ exports.historyCommand = historyCommand;
37
37
  const fs = __importStar(require("fs"));
38
38
  const config_loader_1 = require("../utils/config-loader.cjs");
39
39
  const env_loader_1 = require("../utils/env-loader.cjs");
40
- const colors = {
41
- reset: '\x1b[0m',
42
- bold: '\x1b[1m',
43
- dim: '\x1b[2m',
44
- red: '\x1b[31m',
45
- green: '\x1b[32m',
46
- yellow: '\x1b[33m',
47
- cyan: '\x1b[36m',
48
- };
40
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
41
+ const pool_manager_1 = require("../utils/pool-manager.cjs");
49
42
  async function historyCommand(context) {
50
43
  const { config, flags } = context;
51
44
  if (!config) {
52
- console.error('Error: No configuration found.');
53
- process.exit(1);
45
+ (0, cli_utils_1.fatal)('No configuration found', `run ${cli_utils_1.colors.cyan('relq init')} to create a configuration file`);
54
46
  }
55
- (0, config_loader_1.requireValidConfig)(config);
47
+ await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'history' });
56
48
  const connection = config.connection;
57
49
  const migrationsDir = config.migrations?.directory || './migrations';
58
50
  const tableName = config.migrations?.tableName || '_relq_migrations';
59
51
  const limit = parseInt(flags['n']) || 20;
60
- console.log(`${colors.bold}Migration History${colors.reset}`);
52
+ console.log('Migration History');
61
53
  console.log(`Database: ${(0, env_loader_1.getConnectionDescription)(connection)}`);
62
54
  console.log('');
63
55
  try {
@@ -76,42 +68,41 @@ async function historyCommand(context) {
76
68
  const toShow = history.slice(0, limit);
77
69
  const pendingCount = history.filter(h => h.pending).length;
78
70
  const appliedCount = history.filter(h => !h.pending).length;
79
- console.log(`${colors.dim}Showing ${toShow.length} of ${history.length} migrations${colors.reset}`);
80
- console.log(`${colors.green}${appliedCount} applied${colors.reset}, ${colors.yellow}${pendingCount} pending${colors.reset}`);
71
+ console.log(`Showing ${toShow.length} of ${history.length} migrations`);
72
+ console.log(`${appliedCount} applied, ${pendingCount} pending`);
81
73
  console.log('');
82
74
  for (const record of toShow) {
83
75
  if (record.pending) {
84
- console.log(`${colors.yellow}○ ${record.name}${colors.reset} ${colors.dim}(pending)${colors.reset}`);
76
+ console.log(`${cli_utils_1.colors.yellow}○ ${record.name}${cli_utils_1.colors.reset} ${cli_utils_1.colors.dim}(pending)${cli_utils_1.colors.reset}`);
85
77
  }
86
78
  else {
87
79
  const date = record.appliedAt ? formatDate(record.appliedAt) : '';
88
- console.log(`${colors.green}● ${record.name}${colors.reset} ${colors.dim}${date}${colors.reset}`);
80
+ console.log(`${cli_utils_1.colors.green}● ${record.name}${cli_utils_1.colors.reset} ${cli_utils_1.colors.dim}${date}${cli_utils_1.colors.reset}`);
89
81
  }
90
82
  }
91
83
  if (history.length > limit) {
92
84
  console.log('');
93
- console.log(`${colors.dim}Use "relq history -n ${history.length}" to see all.${colors.reset}`);
85
+ console.log(`${cli_utils_1.colors.dim}Use "relq history -n ${history.length}" to see all.${cli_utils_1.colors.reset}`);
94
86
  }
95
87
  }
96
88
  catch (error) {
97
89
  if (error?.code === '42P01') {
98
- console.log(`${colors.yellow}No migration table found.${colors.reset}`);
99
- console.log(`Run "${colors.cyan}relq push${colors.reset}" to initialize.`);
90
+ console.log(`${cli_utils_1.colors.yellow}No migration table found.${cli_utils_1.colors.reset}`);
91
+ console.log(`Run "${cli_utils_1.colors.cyan}relq push${cli_utils_1.colors.reset}" to initialize.`);
100
92
  console.log('');
101
93
  const files = getMigrationFiles(migrationsDir);
102
94
  if (files.length > 0) {
103
- console.log(`${colors.bold}Pending migrations:${colors.reset}`);
95
+ console.log(`${cli_utils_1.colors.bold}Pending migrations:${cli_utils_1.colors.reset}`);
104
96
  for (const file of files.slice(0, limit)) {
105
- console.log(` ${colors.yellow}○ ${file}${colors.reset}`);
97
+ console.log(` ${cli_utils_1.colors.yellow}○ ${file}${cli_utils_1.colors.reset}`);
106
98
  }
107
99
  }
108
100
  else {
109
- console.log(`${colors.dim}No migration files found.${colors.reset}`);
101
+ console.log(`${cli_utils_1.colors.dim}No migration files found.${cli_utils_1.colors.reset}`);
110
102
  }
111
103
  return;
112
104
  }
113
- console.error('Error:', error instanceof Error ? error.message : error);
114
- process.exit(1);
105
+ (0, cli_utils_1.fatal)('Failed to load history', error instanceof Error ? error.message : String(error));
115
106
  }
116
107
  }
117
108
  function getMigrationFiles(migrationsDir) {
@@ -123,29 +114,17 @@ function getMigrationFiles(migrationsDir) {
123
114
  .sort();
124
115
  }
125
116
  async function getAppliedMigrations(connection, tableName) {
126
- const { Pool } = await Promise.resolve().then(() => __importStar(require("../../addon/pg/index.cjs")));
127
- const pool = new Pool({
128
- host: connection.host,
129
- port: connection.port || 5432,
130
- database: connection.database,
131
- user: connection.user,
132
- password: connection.password,
133
- connectionString: connection.url,
134
- });
135
- try {
117
+ return (0, pool_manager_1.withPool)(connection, async (pool) => {
136
118
  const result = await pool.query(`
137
119
  SELECT name, applied_at
138
120
  FROM "${tableName}"
139
121
  ORDER BY id DESC;
140
122
  `);
141
- return result.rows.map(r => ({
123
+ return result.rows.map((r) => ({
142
124
  name: r.name,
143
125
  appliedAt: new Date(r.applied_at),
144
126
  }));
145
- }
146
- finally {
147
- await pool.end();
148
- }
127
+ });
149
128
  }
150
129
  function formatDate(date) {
151
130
  const now = new Date();
@@ -43,26 +43,22 @@ const schema_comparator_1 = require("../utils/schema-comparator.cjs");
43
43
  const change_tracker_1 = require("../utils/change-tracker.cjs");
44
44
  const relqignore_1 = require("../utils/relqignore.cjs");
45
45
  const git_utils_1 = require("../utils/git-utils.cjs");
46
- async function importCommand(sqlFilePath, options = {}) {
46
+ async function importCommand(sqlFilePath, options = {}, projectRoot = process.cwd()) {
47
47
  const { includeFunctions = false, includeTriggers = false, force = false, dryRun = false } = options;
48
- const projectRoot = process.cwd();
49
48
  const spinner = (0, git_utils_1.createSpinner)();
50
49
  console.log('');
51
50
  if (!sqlFilePath) {
52
- (0, git_utils_1.error)('No SQL file specified.');
53
- console.log('');
54
- console.log('usage: relq import <sql-file> [options]');
55
- console.log('');
56
- console.log('Options:');
57
- console.log(' --output <path> Output schema file path');
58
- console.log(' --force Force import, overwrite local changes');
59
- console.log(' --dry-run Preview changes without applying');
60
- console.log(' --theirs Accept all incoming changes');
61
- console.log(' --ours Keep all local changes (reject incoming)');
62
- console.log(' --abort Abort the import operation');
63
- console.log(' --include-functions Include functions in import');
64
- console.log(' --include-triggers Include triggers in import');
65
- process.exit(1);
51
+ (0, git_utils_1.fatal)('No SQL file specified', 'usage: relq import <sql-file> [options]\n\n' +
52
+ 'Options:\n' +
53
+ ' --output <path> Output schema file path\n' +
54
+ ' --force Force import, overwrite local changes\n' +
55
+ ' --dry-run Preview changes without applying\n' +
56
+ ' --theirs Accept all incoming changes\n' +
57
+ ' --ours Keep all local changes (reject incoming)\n' +
58
+ ' --abort Abort the import operation\n' +
59
+ ' --include-functions Include functions in import\n' +
60
+ ' --include-triggers Include triggers in import');
61
+ return;
66
62
  }
67
63
  if (options.abort) {
68
64
  console.log('Aborting import...');
@@ -85,11 +81,8 @@ async function importCommand(sqlFilePath, options = {}) {
85
81
  (0, git_utils_1.warning)(warn);
86
82
  }
87
83
  if (!validation.valid) {
88
- (0, git_utils_1.error)('Invalid PostgreSQL SQL file:');
89
- for (const err of validation.errors) {
90
- console.log(` - ${err}`);
91
- }
92
- process.exit(1);
84
+ (0, git_utils_1.fatal)('Invalid PostgreSQL SQL file', validation.errors.join('\n - '));
85
+ return;
93
86
  }
94
87
  console.log(`Importing ${git_utils_1.colors.cyan(path.basename(sqlFilePath))} ${git_utils_1.colors.gray(`(${(0, git_utils_1.formatBytes)(sqlContent.length)})`)}`);
95
88
  console.log('');
@@ -104,7 +97,8 @@ async function importCommand(sqlFilePath, options = {}) {
104
97
  (0, git_utils_1.printDirtyWorkingTreeError)(status, 'import');
105
98
  console.log('');
106
99
  (0, git_utils_1.printMergeStrategyHelp)();
107
- process.exit(1);
100
+ (0, git_utils_1.fatal)('Working tree is not clean', 'Commit or stash your changes before importing.');
101
+ return;
108
102
  }
109
103
  }
110
104
  spinner.start('Parsing SQL schema');
@@ -232,7 +226,7 @@ async function importCommand(sqlFilePath, options = {}) {
232
226
  partitions: parsedSchema.partitions,
233
227
  };
234
228
  if (ignoredCount > 0) {
235
- console.log(`${git_utils_1.colors.gray(`ℹ ${ignoredCount} object(s) ignored by .relqignore`)}`);
229
+ console.log(`${ignoredCount} object(s) ignored by .relqignore`);
236
230
  }
237
231
  const dependencyErrors = (0, relqignore_1.validateIgnoreDependencies)({
238
232
  tables: filteredTables.map(t => ({
@@ -250,14 +244,9 @@ async function importCommand(sqlFilePath, options = {}) {
250
244
  }, ignorePatterns);
251
245
  if (dependencyErrors.length > 0) {
252
246
  spinner.stop();
253
- (0, git_utils_1.error)('Dependency validation failed:');
254
- console.log('');
255
- for (const depError of dependencyErrors) {
256
- console.log(` ${git_utils_1.colors.red('✗')} ${depError.message}`);
257
- }
258
- console.log('');
259
- (0, git_utils_1.hint)('Either un-ignore the type or add the column to .relqignore');
260
- process.exit(1);
247
+ const errorMessages = dependencyErrors.map(e => e.message).join('\n ');
248
+ (0, git_utils_1.fatal)('Dependency validation failed', `${errorMessages}\n\nEither un-ignore the type or add the column to .relqignore`);
249
+ return;
261
250
  }
262
251
  spinner.start('Generating TypeScript schema');
263
252
  const dbSchema = convertToDbSchema(filteredSchema, filteredFunctions, triggers, comments);
@@ -313,7 +302,7 @@ async function importCommand(sqlFilePath, options = {}) {
313
302
  }
314
303
  else {
315
304
  console.log('');
316
- console.log(`${git_utils_1.colors.gray(`ℹ ${drops.length} object(s) only in existing schema (preserved)`)}`);
305
+ console.log(`${drops.length} object(s) only in existing schema (preserved)`);
317
306
  }
318
307
  }
319
308
  console.log('');
@@ -340,8 +329,10 @@ async function importCommand(sqlFilePath, options = {}) {
340
329
  console.log(`${git_utils_1.colors.yellow('Dry run mode')} - no files written`);
341
330
  console.log('');
342
331
  console.log('Would write:');
343
- console.log(` ${git_utils_1.colors.cyan(options.output || './db/schema.ts')} ${git_utils_1.colors.gray(`(${(0, git_utils_1.formatBytes)(finalTypescriptContent.length)})`)}`);
344
- console.log(` ${git_utils_1.colors.cyan('.relq/snapshot.json')}`);
332
+ const dryRunOutputPath = options.output || './db/schema.ts';
333
+ const dryRunAbsPath = path.resolve(projectRoot, dryRunOutputPath);
334
+ console.log(` ${git_utils_1.colors.cyan(dryRunAbsPath)} ${git_utils_1.colors.gray(`(${(0, git_utils_1.formatBytes)(finalTypescriptContent.length)})`)}`);
335
+ console.log(` ${git_utils_1.colors.cyan(path.join(projectRoot, '.relq/snapshot.json'))}`);
345
336
  if (changes.length > 0) {
346
337
  console.log(` Stage ${changes.length} change(s)`);
347
338
  }
@@ -349,29 +340,29 @@ async function importCommand(sqlFilePath, options = {}) {
349
340
  return;
350
341
  }
351
342
  const outputPath = options.output || './db/schema.ts';
352
- const absoluteOutputPath = path.resolve(outputPath);
343
+ const absoluteOutputPath = path.resolve(projectRoot, outputPath);
353
344
  const outputDir = path.dirname(absoluteOutputPath);
354
345
  if (!fs.existsSync(outputDir)) {
355
346
  fs.mkdirSync(outputDir, { recursive: true });
356
347
  }
357
348
  fs.writeFileSync(absoluteOutputPath, finalTypescriptContent, 'utf-8');
358
- console.log(`Written ${git_utils_1.colors.cyan(outputPath)} ${git_utils_1.colors.gray(`(${(0, git_utils_1.formatBytes)(finalTypescriptContent.length)})`)}`);
349
+ console.log(`Written ${git_utils_1.colors.cyan(absoluteOutputPath)} ${git_utils_1.colors.gray(`(${(0, git_utils_1.formatBytes)(finalTypescriptContent.length)})`)}`);
359
350
  (0, repo_manager_1.saveSnapshot)(mergedSchema, projectRoot);
360
351
  if (changes.length > 0) {
361
352
  (0, repo_manager_1.addUnstagedChanges)(changes, projectRoot);
362
353
  (0, repo_manager_1.stageChanges)(['.'], projectRoot);
363
354
  console.log('');
364
- console.log(`${git_utils_1.colors.green('✓')} ${changes.length} change(s) staged for commit`);
355
+ console.log(`${changes.length} change(s) staged for commit`);
365
356
  }
366
357
  console.log('');
367
- console.log(`${git_utils_1.colors.green('Import successful.')}`);
358
+ console.log('Import successful.');
368
359
  console.log('');
369
360
  if (changes.length > 0) {
370
- (0, git_utils_1.hint)('Run "relq status" to see staged changes');
371
- (0, git_utils_1.hint)('Run "relq commit -m <message>" to commit');
361
+ (0, git_utils_1.hint)("run 'relq status' to see staged changes");
362
+ (0, git_utils_1.hint)("run 'relq commit -m <message>' to commit");
372
363
  }
373
364
  else {
374
- (0, git_utils_1.hint)('Run "relq status" to see current state');
365
+ (0, git_utils_1.hint)("run 'relq status' to see current state");
375
366
  }
376
367
  console.log('');
377
368
  }
@@ -468,6 +459,7 @@ function convertToDbSchema(parsed, functions = [], triggers = [], comments = [])
468
459
  cycle: s.cycle,
469
460
  ownedBy: s.ownedBy,
470
461
  })) || [],
462
+ collations: [],
471
463
  extensions: parsed.extensions,
472
464
  partitions: parsed.partitions,
473
465
  functions: functions.map(f => ({
@@ -736,6 +728,7 @@ function snapshotToDbSchemaForGeneration(snapshot) {
736
728
  cycle: s.cycle,
737
729
  ownedBy: s.ownedBy ?? undefined,
738
730
  })) || [],
731
+ collations: [],
739
732
  extensions: (snapshot.extensions || []).map(e => typeof e === 'string' ? e : e.name),
740
733
  partitions: [],
741
734
  functions: snapshot.functions?.map(f => ({
@@ -40,9 +40,9 @@ const readline = __importStar(require("readline"));
40
40
  const env_loader_1 = require("../utils/env-loader.cjs");
41
41
  const repo_manager_1 = require("../utils/repo-manager.cjs");
42
42
  const relqignore_1 = require("../utils/relqignore.cjs");
43
- const spinner_1 = require("../utils/spinner.cjs");
43
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
44
44
  function ask(rl, question, defaultValue) {
45
- const suffix = defaultValue ? ` ${spinner_1.colors.muted(`[${defaultValue}]`)}` : '';
45
+ const suffix = defaultValue ? ` ${cli_utils_1.colors.muted(`[${defaultValue}]`)}` : '';
46
46
  return new Promise((resolve) => {
47
47
  rl.question(`${question}${suffix}: `, (answer) => {
48
48
  resolve(answer.trim() || defaultValue || '');
@@ -50,7 +50,7 @@ function ask(rl, question, defaultValue) {
50
50
  });
51
51
  }
52
52
  function askYesNo(rl, question, defaultYes = true) {
53
- const suffix = defaultYes ? spinner_1.colors.muted('[Y/n]') : spinner_1.colors.muted('[y/N]');
53
+ const suffix = defaultYes ? cli_utils_1.colors.muted('[Y/n]') : cli_utils_1.colors.muted('[y/N]');
54
54
  return new Promise((resolve) => {
55
55
  rl.question(`${question} ${suffix}: `, (answer) => {
56
56
  const a = answer.trim().toLowerCase();
@@ -154,13 +154,11 @@ ${connectionBlock}
154
154
  }
155
155
  async function initCommand(context) {
156
156
  const cwd = process.cwd();
157
- const spinner = (0, spinner_1.createSpinner)();
158
- console.log('');
159
- console.log(`${spinner_1.colors.bold('Relq')} - Git-like schema versioning for PostgreSQL`);
157
+ const spinner = (0, cli_utils_1.createSpinner)();
160
158
  console.log('');
161
159
  const { findConfigFileRecursive } = await Promise.resolve().then(() => __importStar(require("../utils/config-loader.cjs")));
160
+ const { findProjectRoot } = await Promise.resolve().then(() => __importStar(require("../utils/project-root.cjs")));
162
161
  const existingConfig = findConfigFileRecursive(cwd);
163
- const localRelqExists = (0, repo_manager_1.isInitialized)(cwd);
164
162
  let projectRoot = cwd;
165
163
  let existingConfigValues = null;
166
164
  if (existingConfig) {
@@ -172,28 +170,35 @@ async function initCommand(context) {
172
170
  catch {
173
171
  }
174
172
  if (existingConfig.directory !== cwd) {
175
- console.log(`${spinner_1.colors.cyan('ℹ')} Found ${spinner_1.colors.cyan('relq.config.ts')} in parent directory:`);
176
- console.log(` ${spinner_1.colors.muted(existingConfig.path)}`);
173
+ (0, cli_utils_1.hint)(`Found relq.config.ts in: ${cli_utils_1.colors.cyan(existingConfig.directory)}`);
174
+ console.log(` Initializing at project root...`);
177
175
  console.log('');
178
- console.log(`${spinner_1.colors.muted('Run commands from project root:')} ${spinner_1.colors.cyan(existingConfig.directory)}`);
176
+ }
177
+ }
178
+ else {
179
+ const packageRoot = findProjectRoot(cwd);
180
+ if (packageRoot && packageRoot !== cwd) {
181
+ projectRoot = packageRoot;
182
+ (0, cli_utils_1.hint)(`Found package.json in: ${cli_utils_1.colors.cyan(packageRoot)}`);
183
+ console.log(` Initializing at project root...`);
179
184
  console.log('');
180
- return;
181
185
  }
186
+ }
187
+ if (existingConfig && existingConfig.directory === projectRoot) {
182
188
  const relqFolderExists = (0, repo_manager_1.isInitialized)(projectRoot);
183
189
  if (relqFolderExists) {
184
- console.log(`${spinner_1.colors.green('✓')} Relq is already initialized.`);
190
+ console.log(`${cli_utils_1.colors.green('Relq is already initialized.')}`);
185
191
  console.log('');
186
- console.log(` ${spinner_1.colors.green('')} ${spinner_1.colors.muted('relq.config.ts')}`);
187
- console.log(` ${spinner_1.colors.green('')} ${spinner_1.colors.muted('.relq/')} folder`);
192
+ console.log(` ${cli_utils_1.colors.dim('-')} relq.config.ts`);
193
+ console.log(` ${cli_utils_1.colors.dim('-')} .relq/ folder`);
188
194
  console.log('');
189
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq status')} ${spinner_1.colors.muted('to see current state.')}`);
190
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq pull')} ${spinner_1.colors.muted('to sync with database.')}`);
195
+ (0, cli_utils_1.hint)(`Use ${cli_utils_1.colors.cyan('relq status')} to see current state`);
196
+ (0, cli_utils_1.hint)(`Use ${cli_utils_1.colors.cyan('relq pull')} to sync with database`);
191
197
  return;
192
198
  }
193
199
  else {
194
- console.log(`${spinner_1.colors.yellow('⚠')} Found ${spinner_1.colors.cyan('relq.config.ts')} but ${spinner_1.colors.cyan('.relq/')} folder is missing.`);
195
- console.log('');
196
- console.log(`${spinner_1.colors.muted('This could mean the setup was incomplete.')}`);
200
+ (0, cli_utils_1.warning)('Found relq.config.ts but .relq/ folder is missing');
201
+ (0, cli_utils_1.hint)('This could mean the setup was incomplete');
197
202
  console.log('');
198
203
  const rl = readline.createInterface({
199
204
  input: process.stdin,
@@ -217,12 +222,12 @@ async function initCommand(context) {
217
222
  }
218
223
  if (missingFields.length > 0) {
219
224
  console.log('');
220
- console.log(`${spinner_1.colors.yellow('⚠')} Your config is missing required fields:`);
225
+ (0, cli_utils_1.warning)('Your config is missing required fields:');
221
226
  for (const field of missingFields) {
222
- console.log(` ${spinner_1.colors.red('')} ${field}`);
227
+ console.log(` ${cli_utils_1.colors.dim('-')} ${field}`);
223
228
  }
224
229
  console.log('');
225
- console.log(`${spinner_1.colors.cyan('●')} Let's fill in the missing fields:`);
230
+ console.log(`${cli_utils_1.colors.dim("Let's fill in the missing fields:")}`);
226
231
  console.log('');
227
232
  let authorValue = existingConfigValues?.author;
228
233
  let schemaValue = existingConfigValues?.schema;
@@ -234,9 +239,9 @@ async function initCommand(context) {
234
239
  }
235
240
  if (missingFields.includes('connection')) {
236
241
  console.log('');
237
- console.log(`${spinner_1.colors.red('●')} Connection not configured.`);
238
- console.log(` ${spinner_1.colors.muted('Set DATABASE_* environment variables in .env')}`);
239
- console.log(` ${spinner_1.colors.muted('or update relq.config.ts with connection details.')}`);
242
+ console.log('Connection not configured.');
243
+ console.log(' Set DATABASE_* environment variables in .env');
244
+ console.log(' or update relq.config.ts with connection details.');
240
245
  rl.close();
241
246
  return;
242
247
  }
@@ -262,18 +267,18 @@ async function initCommand(context) {
262
267
  }
263
268
  rl.close();
264
269
  console.log('');
265
- console.log(`${spinner_1.colors.cyan('●')} Completing setup...`);
270
+ console.log('Completing setup...');
266
271
  console.log('');
267
272
  (0, repo_manager_1.initRepository)(projectRoot);
268
- console.log(` ${spinner_1.colors.green('✓')} Created ${spinner_1.colors.cyan('.relq/')} folder`);
273
+ console.log(' Created .relq/ folder');
269
274
  (0, relqignore_1.createDefaultRelqignore)(projectRoot);
270
- console.log(` ${spinner_1.colors.green('✓')} Created ${spinner_1.colors.cyan('.relqignore')}`);
275
+ console.log(' Created .relqignore');
271
276
  const gitignorePath = path.join(projectRoot, '.gitignore');
272
277
  if (fs.existsSync(gitignorePath)) {
273
278
  const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
274
279
  if (!gitignore.includes('.relq')) {
275
280
  fs.appendFileSync(gitignorePath, '\n# Relq local state\n.relq/\n');
276
- console.log(` ${spinner_1.colors.green('✓')} Added ${spinner_1.colors.cyan('.relq/')} to .gitignore`);
281
+ console.log(' Added .relq/ to .gitignore');
277
282
  }
278
283
  }
279
284
  if (existingConfigValues?.connection) {
@@ -287,14 +292,20 @@ async function initCommand(context) {
287
292
  }
288
293
  }
289
294
  console.log('');
290
- console.log(`${spinner_1.colors.green('✓')} Setup complete!`);
295
+ console.log('Setup complete!');
291
296
  console.log('');
292
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq pull')} ${spinner_1.colors.muted('to sync with database.')}`);
297
+ const calledFrom = context.flags['called-from'];
298
+ if (calledFrom) {
299
+ console.log(`Continuing with ${calledFrom}...`);
300
+ }
301
+ else {
302
+ console.log('Run "relq pull" to sync with database.');
303
+ }
293
304
  return;
294
305
  }
295
306
  rl.close();
296
307
  console.log('');
297
- console.log(`${spinner_1.colors.muted('Cancelled.')}`);
308
+ console.log(`${cli_utils_1.colors.muted('Cancelled.')}`);
298
309
  return;
299
310
  }
300
311
  catch {
@@ -308,7 +319,7 @@ async function initCommand(context) {
308
319
  output: process.stdout,
309
320
  });
310
321
  try {
311
- console.log(`${spinner_1.colors.cyan('●')} Checking for database connection...`);
322
+ console.log('Checking for database connection...');
312
323
  console.log('');
313
324
  const envCheck = checkEnvVars();
314
325
  let useEnv = false;
@@ -318,28 +329,28 @@ async function initCommand(context) {
318
329
  let user = 'postgres';
319
330
  let password = '';
320
331
  if (envCheck.found) {
321
- console.log(`${spinner_1.colors.green('✓')} Found database environment variables:`);
332
+ console.log('Found database environment variables:');
322
333
  if (envCheck.vars.RELQ_PG_CONN_URL) {
323
- console.log(` ${spinner_1.colors.green('✓')} RELQ_PG_CONN_URL`);
334
+ console.log(' RELQ_PG_CONN_URL');
324
335
  }
325
336
  else {
326
337
  if (envCheck.vars.DATABASE_HOST)
327
- console.log(` ${spinner_1.colors.green('✓')} DATABASE_HOST: ${envCheck.vars.DATABASE_HOST}`);
338
+ console.log(` DATABASE_HOST: ${envCheck.vars.DATABASE_HOST}`);
328
339
  if (envCheck.vars.DATABASE_PORT)
329
- console.log(` ${spinner_1.colors.green('✓')} DATABASE_PORT: ${envCheck.vars.DATABASE_PORT}`);
340
+ console.log(` DATABASE_PORT: ${envCheck.vars.DATABASE_PORT}`);
330
341
  if (envCheck.vars.DATABASE_NAME)
331
- console.log(` ${spinner_1.colors.green('✓')} DATABASE_NAME: ${envCheck.vars.DATABASE_NAME}`);
342
+ console.log(` DATABASE_NAME: ${envCheck.vars.DATABASE_NAME}`);
332
343
  if (envCheck.vars.DATABASE_USER)
333
- console.log(` ${spinner_1.colors.green('✓')} DATABASE_USER: ${envCheck.vars.DATABASE_USER}`);
344
+ console.log(` DATABASE_USER: ${envCheck.vars.DATABASE_USER}`);
334
345
  if (envCheck.vars.DATABASE_PASSWORD)
335
- console.log(` ${spinner_1.colors.green('✓')} DATABASE_PASSWORD: ***`);
346
+ console.log(' DATABASE_PASSWORD: ***');
336
347
  }
337
348
  console.log('');
338
349
  useEnv = await askYesNo(rl, 'Use these environment variables?', true);
339
350
  console.log('');
340
351
  }
341
352
  if (!useEnv) {
342
- console.log(`${spinner_1.colors.cyan('●')} Database Connection`);
353
+ console.log('Database Connection');
343
354
  console.log('');
344
355
  host = await ask(rl, ' Host', 'localhost');
345
356
  port = await ask(rl, ' Port', '5432');
@@ -349,21 +360,21 @@ async function initCommand(context) {
349
360
  console.log('');
350
361
  }
351
362
  const detectedPath = detectSchemaPath();
352
- console.log(`${spinner_1.colors.cyan('●')} Schema Configuration`);
363
+ console.log('Schema Configuration');
353
364
  console.log('');
354
365
  const schemaPath = await ask(rl, ' Schema file path', detectedPath);
355
366
  console.log('');
356
- console.log(`${spinner_1.colors.cyan('●')} Author (for commit history)`);
367
+ console.log('Author (for commit history)');
357
368
  console.log('');
358
369
  const author = await ask(rl, ' Name <email>', 'Developer <dev@example.com>');
359
370
  console.log('');
360
- console.log(`${spinner_1.colors.cyan('●')} Features to track`);
371
+ console.log('Features to track');
361
372
  console.log('');
362
373
  const includeFunctions = await askYesNo(rl, ' Include functions?', false);
363
374
  const includeTriggers = await askYesNo(rl, ' Include triggers?', false);
364
375
  console.log('');
365
376
  rl.close();
366
- console.log(`${spinner_1.colors.cyan('●')} Creating files...`);
377
+ console.log('Creating files...');
367
378
  console.log('');
368
379
  const configPath = path.join(cwd, 'relq.config.ts');
369
380
  if (!fs.existsSync(configPath)) {
@@ -380,26 +391,26 @@ async function initCommand(context) {
380
391
  includeTriggers,
381
392
  });
382
393
  fs.writeFileSync(configPath, configContent, 'utf-8');
383
- console.log(` ${spinner_1.colors.green('✓')} Created ${spinner_1.colors.cyan('relq.config.ts')}`);
394
+ console.log(' Created relq.config.ts');
384
395
  }
385
396
  else {
386
- console.log(` ${spinner_1.colors.yellow('○')} ${spinner_1.colors.cyan('relq.config.ts')} already exists`);
397
+ console.log(' relq.config.ts already exists');
387
398
  }
388
399
  (0, repo_manager_1.initRepository)(cwd);
389
- console.log(` ${spinner_1.colors.green('✓')} Created ${spinner_1.colors.cyan('.relq/')} folder`);
400
+ console.log(' Created .relq/ folder');
390
401
  (0, relqignore_1.createDefaultRelqignore)(cwd);
391
- console.log(` ${spinner_1.colors.green('✓')} Created ${spinner_1.colors.cyan('.relqignore')}`);
402
+ console.log(' Created .relqignore');
392
403
  const schemaDir = path.dirname(schemaPath);
393
404
  if (!fs.existsSync(schemaDir)) {
394
405
  fs.mkdirSync(schemaDir, { recursive: true });
395
- console.log(` ${spinner_1.colors.green('✓')} Created ${spinner_1.colors.cyan(schemaDir + '/')}`);
406
+ console.log(` Created ${schemaDir}/`);
396
407
  }
397
408
  const gitignorePath = path.join(cwd, '.gitignore');
398
409
  if (fs.existsSync(gitignorePath)) {
399
410
  const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
400
411
  if (!gitignore.includes('.relq')) {
401
412
  fs.appendFileSync(gitignorePath, '\n# Relq local state\n.relq/\n');
402
- console.log(` ${spinner_1.colors.green('✓')} Added ${spinner_1.colors.cyan('.relq/')} to ${spinner_1.colors.cyan('.gitignore')}`);
413
+ console.log(' Added .relq/ to .gitignore');
403
414
  }
404
415
  }
405
416
  console.log('');
@@ -418,20 +429,19 @@ async function initCommand(context) {
418
429
  }
419
430
  catch (error) {
420
431
  spinner.fail('Could not connect to database');
421
- console.log(` ${spinner_1.colors.yellow('⚠')} ${spinner_1.colors.muted('Run relq pull after fixing connection')}`);
432
+ (0, cli_utils_1.hint)("run 'relq pull' after fixing connection");
422
433
  }
423
434
  console.log('');
424
- console.log(`${spinner_1.colors.green('✓')} ${spinner_1.colors.bold('Relq initialized successfully!')}`);
435
+ console.log('Relq initialized successfully!');
425
436
  console.log('');
426
- console.log(`${spinner_1.colors.bold('Next steps:')}`);
427
- console.log(` 1. Review ${spinner_1.colors.cyan('relq.config.ts')}`);
428
- console.log(` 2. Run ${spinner_1.colors.cyan('relq pull')} to sync with database`);
429
- console.log(` 3. Run ${spinner_1.colors.cyan('relq status')} to see current state`);
437
+ console.log('Next steps:');
438
+ console.log(" 1. Review 'relq.config.ts'");
439
+ console.log(" 2. Run 'relq pull' to sync with database");
440
+ console.log(" 3. Run 'relq status' to see current state");
430
441
  console.log('');
431
442
  }
432
443
  catch (error) {
433
444
  rl.close();
434
- console.error(spinner_1.colors.red(`Error: ${error instanceof Error ? error.message : error}`));
435
- process.exit(1);
445
+ (0, cli_utils_1.fatal)('Initialization failed', error instanceof Error ? error.message : String(error));
436
446
  }
437
447
  }
@@ -37,6 +37,7 @@ exports.introspectCommand = introspectCommand;
37
37
  const fs = __importStar(require("fs"));
38
38
  const readline = __importStar(require("readline"));
39
39
  const index_1 = require("../../introspect/index.cjs");
40
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
40
41
  function readStdin() {
41
42
  return new Promise((resolve) => {
42
43
  let data = '';
@@ -79,8 +80,7 @@ async function introspectCommand(context) {
79
80
  let sql;
80
81
  if (filePath) {
81
82
  if (!fs.existsSync(filePath)) {
82
- console.error(`Error: File not found: ${filePath}`);
83
- process.exit(1);
83
+ (0, cli_utils_1.fatal)(`File not found: ${filePath}`, 'Check the file path and try again.');
84
84
  }
85
85
  sql = fs.readFileSync(filePath, 'utf-8');
86
86
  console.log(`📄 Reading from: ${filePath}\n`);
@@ -92,10 +92,7 @@ async function introspectCommand(context) {
92
92
  sql = await readInteractive();
93
93
  }
94
94
  if (!sql.trim()) {
95
- console.error('Error: No SQL input provided.');
96
- console.error('Usage: relq introspect --file schema.sql');
97
- console.error(' or: cat schema.sql | relq introspect');
98
- process.exit(1);
95
+ (0, cli_utils_1.fatal)('No SQL input provided', 'Usage: relq introspect --file schema.sql\n or: cat schema.sql | relq introspect');
99
96
  }
100
97
  console.log('🔍 Parsing SQL...\n');
101
98
  try {
@@ -116,8 +113,7 @@ async function introspectCommand(context) {
116
113
  console.log(output.join('\n'));
117
114
  }
118
115
  catch (error) {
119
- console.error('Error parsing SQL:', error instanceof Error ? error.message : error);
120
- process.exit(1);
116
+ (0, cli_utils_1.fatal)('Error parsing SQL', error instanceof Error ? error.message : String(error));
121
117
  }
122
118
  }
123
119
  function generateDefineTableCode(table) {