@quonfig/cli 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ $ npm install -g @quonfig/cli
14
14
  $ qfg COMMAND
15
15
  running command...
16
16
  $ qfg (--version)
17
- @quonfig/cli/0.0.33 linux-x64 node-v24.4.1
17
+ @quonfig/cli/0.0.35 linux-x64 node-v24.4.1
18
18
  $ qfg --help [COMMAND]
19
19
  USAGE
20
20
  $ qfg COMMAND
@@ -91,7 +91,7 @@ EXAMPLES
91
91
  $ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
92
92
  ```
93
93
 
94
- _See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/config-schema.ts)_
94
+ _See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/config-schema.ts)_
95
95
 
96
96
  ## `qfg create NAME`
97
97
 
@@ -176,7 +176,7 @@ EXAMPLES
176
176
  $ qfg set-rollout my.new.flag --environment production --true-percent 20
177
177
  ```
178
178
 
179
- _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/create.ts)_
179
+ _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/create.ts)_
180
180
 
181
181
  ## `qfg delete NAME`
182
182
 
@@ -222,7 +222,7 @@ EXAMPLES
222
222
  $ qfg delete my.config --yes
223
223
  ```
224
224
 
225
- _See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/delete.ts)_
225
+ _See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/delete.ts)_
226
226
 
227
227
  ## `qfg flag info [NAME]`
228
228
 
@@ -425,7 +425,7 @@ EXAMPLES
425
425
  $ qfg generate --targets node-ts -o ./dist # combine with targets
426
426
  ```
427
427
 
428
- _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/generate.ts)_
428
+ _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/generate.ts)_
429
429
 
430
430
  ## `qfg generate-new-hex-key`
431
431
 
@@ -448,7 +448,7 @@ EXAMPLES
448
448
  $ qfg generate-new-hex-key
449
449
  ```
450
450
 
451
- _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/generate-new-hex-key.ts)_
451
+ _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/generate-new-hex-key.ts)_
452
452
 
453
453
  ## `qfg get [NAME]`
454
454
 
@@ -480,7 +480,7 @@ EXAMPLES
480
480
  $ qfg get my.config.name --environment=production
481
481
  ```
482
482
 
483
- _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/get.ts)_
483
+ _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/get.ts)_
484
484
 
485
485
  ## `qfg info [NAME]`
486
486
 
@@ -528,7 +528,7 @@ EXAMPLES
528
528
  $ qfg info my.config.name --exclude-evaluations # skip 24h stats
529
529
  ```
530
530
 
531
- _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/info.ts)_
531
+ _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/info.ts)_
532
532
 
533
533
  ## `qfg init [DIRECTORY]`
534
534
 
@@ -568,7 +568,7 @@ EXAMPLES
568
568
  $ qfg init --dry-run
569
569
  ```
570
570
 
571
- _See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/init.ts)_
571
+ _See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/init.ts)_
572
572
 
573
573
  ## `qfg interactive`
574
574
 
@@ -598,7 +598,7 @@ EXAMPLES
598
598
  $ qfg
599
599
  ```
600
600
 
601
- _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/interactive.ts)_
601
+ _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/interactive.ts)_
602
602
 
603
603
  ## `qfg list`
604
604
 
@@ -638,7 +638,7 @@ EXAMPLES
638
638
  $ qfg list --feature-flags
639
639
  ```
640
640
 
641
- _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/list.ts)_
641
+ _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/list.ts)_
642
642
 
643
643
  ## `qfg log-level NAME`
644
644
 
@@ -703,7 +703,7 @@ EXAMPLES
703
703
  $ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
704
704
  ```
705
705
 
706
- _See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/log-level.ts)_
706
+ _See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/log-level.ts)_
707
707
 
708
708
  ## `qfg login`
709
709
 
@@ -728,7 +728,7 @@ EXAMPLES
728
728
  $ qfg login --profile myprofile
729
729
  ```
730
730
 
731
- _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/login.ts)_
731
+ _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/login.ts)_
732
732
 
733
733
  ## `qfg logout`
734
734
 
@@ -751,7 +751,7 @@ EXAMPLES
751
751
  $ qfg logout
752
752
  ```
753
753
 
754
- _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/logout.ts)_
754
+ _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/logout.ts)_
755
755
 
756
756
  ## `qfg migrate`
757
757
 
@@ -805,7 +805,7 @@ EXAMPLES
805
805
  $ qfg migrate --from launch --api-key $LAUNCH_API_KEY --dir ./quonfig-repo --staging
806
806
  ```
807
807
 
808
- _See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/migrate.ts)_
808
+ _See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/migrate.ts)_
809
809
 
810
810
  ## `qfg migrate doctor`
811
811
 
@@ -843,7 +843,7 @@ EXAMPLES
843
843
  $ qfg migrate doctor --json
844
844
  ```
845
845
 
846
- _See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/migrate/doctor.ts)_
846
+ _See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/migrate/doctor.ts)_
847
847
 
848
848
  ## `qfg migrate my-code`
849
849
 
@@ -875,7 +875,7 @@ EXAMPLES
875
875
  $ qfg migrate my-code --dry-run
876
876
  ```
877
877
 
878
- _See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/migrate/my-code.ts)_
878
+ _See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/migrate/my-code.ts)_
879
879
 
880
880
  ## `qfg migrate status`
881
881
 
@@ -906,7 +906,7 @@ EXAMPLES
906
906
  $ qfg migrate status --json
907
907
  ```
908
908
 
909
- _See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/migrate/status.ts)_
909
+ _See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/migrate/status.ts)_
910
910
 
911
911
  ## `qfg override [NAME] [VALUE]`
912
912
 
@@ -961,7 +961,7 @@ EXAMPLES
961
961
  $ qfg override my.flag true --env=staging
962
962
  ```
963
963
 
964
- _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/override.ts)_
964
+ _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/override.ts)_
965
965
 
966
966
  ## `qfg pull`
967
967
 
@@ -969,10 +969,13 @@ Clone or update a local copy of your workspace config files.
969
969
 
970
970
  ```
971
971
  USAGE
972
- $ qfg pull [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--workspace <value>]
972
+ $ qfg pull [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--rebase] [--workspace
973
+ <value>]
973
974
 
974
975
  FLAGS
975
976
  --dir=<value> Local directory to clone/update (defaults to QUONFIG_DIR env var)
977
+ --rebase Replay local commits on top of origin/main when the two have diverged. Conflicts are surfaced via
978
+ standard git markers; resolve with `git rebase --continue`.
976
979
  --workspace=<value> Workspace ID (defaults to active profile)
977
980
 
978
981
  GLOBAL FLAGS
@@ -1005,7 +1008,7 @@ EXAMPLES
1005
1008
  $ qfg pull # uses QUONFIG_DIR env var
1006
1009
  ```
1007
1010
 
1008
- _See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/pull.ts)_
1011
+ _See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/pull.ts)_
1009
1012
 
1010
1013
  ## `qfg push`
1011
1014
 
@@ -1053,7 +1056,7 @@ EXAMPLES
1053
1056
  $ qfg push --dir ./our-config --yes
1054
1057
  ```
1055
1058
 
1056
- _See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/push.ts)_
1059
+ _See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/push.ts)_
1057
1060
 
1058
1061
  ## `qfg schema NAME`
1059
1062
 
@@ -1091,7 +1094,7 @@ EXAMPLES
1091
1094
  $ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
1092
1095
  ```
1093
1096
 
1094
- _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/schema.ts)_
1097
+ _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/schema.ts)_
1095
1098
 
1096
1099
  ## `qfg sdk-key`
1097
1100
 
@@ -1118,7 +1121,7 @@ EXAMPLES
1118
1121
  $ qfg sdk-key revoke <key-id>
1119
1122
  ```
1120
1123
 
1121
- _See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/sdk-key.ts)_
1124
+ _See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/sdk-key.ts)_
1122
1125
 
1123
1126
  ## `qfg sdk-key create`
1124
1127
 
@@ -1150,7 +1153,7 @@ EXAMPLES
1150
1153
  $ qfg sdk-key create --environment staging --type browser
1151
1154
  ```
1152
1155
 
1153
- _See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/sdk-key/create.ts)_
1156
+ _See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/sdk-key/create.ts)_
1154
1157
 
1155
1158
  ## `qfg sdk-key list`
1156
1159
 
@@ -1179,7 +1182,7 @@ EXAMPLES
1179
1182
  $ qfg sdk-key list --environment production
1180
1183
  ```
1181
1184
 
1182
- _See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/sdk-key/list.ts)_
1185
+ _See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/sdk-key/list.ts)_
1183
1186
 
1184
1187
  ## `qfg sdk-key revoke KEYID`
1185
1188
 
@@ -1206,7 +1209,7 @@ EXAMPLES
1206
1209
  $ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
1207
1210
  ```
1208
1211
 
1209
- _See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/sdk-key/revoke.ts)_
1212
+ _See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/sdk-key/revoke.ts)_
1210
1213
 
1211
1214
  ## `qfg set-default [NAME]`
1212
1215
 
@@ -1277,7 +1280,7 @@ EXAMPLES
1277
1280
  $ qfg pull && qfg config-schema
1278
1281
  ```
1279
1282
 
1280
- _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/set-default.ts)_
1283
+ _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/set-default.ts)_
1281
1284
 
1282
1285
  ## `qfg set-rollout [NAME]`
1283
1286
 
@@ -1354,7 +1357,7 @@ EXAMPLES
1354
1357
  $ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
1355
1358
  ```
1356
1359
 
1357
- _See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/set-rollout.ts)_
1360
+ _See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/set-rollout.ts)_
1358
1361
 
1359
1362
  ## `qfg sync`
1360
1363
 
@@ -1386,7 +1389,7 @@ EXAMPLES
1386
1389
  $ qfg sync --watch --dir ./our-config --interval 30
1387
1390
  ```
1388
1391
 
1389
- _See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/sync.ts)_
1392
+ _See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/sync.ts)_
1390
1393
 
1391
1394
  ## `qfg toggle [NAME]`
1392
1395
 
@@ -1488,7 +1491,7 @@ EXAMPLES
1488
1491
  $ qfg verify --json
1489
1492
  ```
1490
1493
 
1491
- _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/verify.ts)_
1494
+ _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/verify.ts)_
1492
1495
 
1493
1496
  ## `qfg whoami`
1494
1497
 
@@ -1511,7 +1514,7 @@ EXAMPLES
1511
1514
  $ qfg whoami
1512
1515
  ```
1513
1516
 
1514
- _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/whoami.ts)_
1517
+ _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/whoami.ts)_
1515
1518
 
1516
1519
  ## `qfg workspace`
1517
1520
 
@@ -1534,7 +1537,7 @@ EXAMPLES
1534
1537
  $ qfg workspace
1535
1538
  ```
1536
1539
 
1537
- _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/workspace.ts)_
1540
+ _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/workspace.ts)_
1538
1541
 
1539
1542
  ## `qfg workspace bootstrap`
1540
1543
 
@@ -1567,7 +1570,7 @@ EXAMPLES
1567
1570
  $ qfg workspace bootstrap --dir ./our-config --skip-validate
1568
1571
  ```
1569
1572
 
1570
- _See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/workspace/bootstrap.ts)_
1573
+ _See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/workspace/bootstrap.ts)_
1571
1574
 
1572
1575
  ## `qfg workspace create SLUG`
1573
1576
 
@@ -1601,7 +1604,7 @@ EXAMPLES
1601
1604
  $ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
1602
1605
  ```
1603
1606
 
1604
- _See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/workspace/create.ts)_
1607
+ _See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/workspace/create.ts)_
1605
1608
 
1606
1609
  ## `qfg workspace switch [SLUG]`
1607
1610
 
@@ -1629,7 +1632,7 @@ EXAMPLES
1629
1632
  $ qfg workspace switch acme/production
1630
1633
  ```
1631
1634
 
1632
- _See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.33/src/commands/workspace/switch.ts)_
1635
+ _See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.35/src/commands/workspace/switch.ts)_
1633
1636
  <!-- commandsstop -->
1634
1637
 
1635
1638
  ## Local Development
@@ -3,7 +3,7 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { BaseCommand } from '../index.js';
5
5
  import { CrossSourceError, assertSourceMatches, readImportState } from '../migrate/import-state.js';
6
- import { applyLocalMigration } from '../migrate/local-write.js';
6
+ import { applyLocalMigration, MigratorKeyCollisionError } from '../migrate/local-write.js';
7
7
  import { buildPushConflictSuggestion } from '../migrate/migrate-suggestion.js';
8
8
  import { PushConflictError, PushHookRejectedError } from '../util/clone-and-stack-push.js';
9
9
  import { MigratorVerifyError, pushMigrationToCloud } from '../migrate/push-to-cloud.js';
@@ -150,12 +150,11 @@ export default class Migrate extends BaseCommand {
150
150
  try {
151
151
  const result = await pushMigrationToCloud({
152
152
  changes: toProcess,
153
- commitMessage: buildCommitMessage(flags.from, toProcess.length),
154
153
  environments,
155
154
  importState,
156
155
  localDir: dir,
157
156
  remoteUrl: repoUrl,
158
- reportData: buildReportData(flags.from, toProcess.length),
157
+ reportData: buildReportData(flags.from),
159
158
  source,
160
159
  });
161
160
  duplicateResolutionsForWarn = result.duplicateResolutions;
@@ -183,6 +182,9 @@ export default class Migrate extends BaseCommand {
183
182
  `Fix the issues above (typically in the source system or by editing the local files), then re-run with --push. ` +
184
183
  `You can re-run \`qfg verify ${dir}\` locally to confirm the fixes before pushing.`);
185
184
  }
185
+ if (error instanceof MigratorKeyCollisionError) {
186
+ return this.err(error.message);
187
+ }
186
188
  throw error;
187
189
  }
188
190
  }
@@ -194,11 +196,10 @@ export default class Migrate extends BaseCommand {
194
196
  this.log(`Fetched ${changes.length} change(s) from ${flags.from}; processing ${toProcess.length} into ${dir}.`);
195
197
  const localResult = await applyLocalMigration({
196
198
  changes: toProcess,
197
- commitMessage: buildCommitMessage(flags.from, toProcess.length),
198
199
  environments,
199
200
  importState: localImportState,
200
201
  localDir: dir,
201
- reportData: buildReportData(flags.from, toProcess.length),
202
+ reportData: buildReportData(flags.from),
202
203
  source,
203
204
  });
204
205
  duplicateResolutionsForWarn = localResult.duplicateResolutions;
@@ -215,6 +216,8 @@ export default class Migrate extends BaseCommand {
215
216
  catch (error) {
216
217
  if (error instanceof NotYetImplementedError)
217
218
  return this.err(error.message);
219
+ if (error instanceof MigratorKeyCollisionError)
220
+ return this.err(error.message);
218
221
  throw error;
219
222
  }
220
223
  finally {
@@ -247,16 +250,20 @@ function latestChangedAtOf(changes, fallback) {
247
250
  }
248
251
  return latest ?? fallback;
249
252
  }
250
- function buildCommitMessage(source, count) {
251
- return `migrator: import ${count} change(s) from ${source}`;
252
- }
253
- function buildReportData(source, count) {
253
+ function buildReportData(source) {
254
+ // Counts are placeholders; applyLocalMigration / pushMigrationToCloud
255
+ // override `counts` after writing files so they reflect what actually
256
+ // landed on disk (qfg-7eig). The commit message is also derived from those
257
+ // counts inside the apply function — it is not built here.
254
258
  return {
255
259
  cleanMappings: [],
256
260
  counts: {
261
+ configsMigrated: 0,
257
262
  environmentsMapped: 0,
258
- flagsMigrated: count,
263
+ flagsMigrated: 0,
259
264
  itemsSkipped: 0,
265
+ logLevelsMigrated: 0,
266
+ schemasMigrated: 0,
260
267
  segmentsMigrated: 0,
261
268
  },
262
269
  dryRun: false,
@@ -5,6 +5,7 @@ export default class Pull extends BaseCommand {
5
5
  static examples: string[];
6
6
  static flags: {
7
7
  dir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ rebase: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
9
  workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
10
  };
10
11
  run(): Promise<JsonObj | void>;
@@ -6,7 +6,7 @@ import { loadGiteaToken, isGiteaTokenExpired, saveGiteaToken } from '../util/git
6
6
  import { mintAndStoreGiteaReadToken, mintGiteaToken } from '../util/gitea-api.js';
7
7
  import { readWorkspaceSlug, tryParseWorkspacePin, writeWorkspaceSlug } from '../util/quonfig-json.js';
8
8
  import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
9
- import { isGitRepo, getRemoteUrl, gitFetch, gitSetRemote, canFastForward, hasDivergedFromRemote, gitMergeFfOnly, gitClone, displayUrl, addAndCommitFile, dirtyTrackedFiles, } from '../util/git-ops.js';
9
+ import { isGitRepo, getRemoteUrl, gitFetch, gitSetRemote, canFastForward, hasDivergedFromRemote, gitMergeFfOnly, gitPullRebase, gitClone, displayUrl, addAndCommitFile, dirtyTrackedFiles, } from '../util/git-ops.js';
10
10
  export default class Pull extends BaseCommand {
11
11
  static description = `Clone or update a local copy of your workspace config files.
12
12
 
@@ -29,6 +29,10 @@ CLI shortcuts (no JSON editing needed for simple cases):
29
29
  dir: Flags.string({
30
30
  description: 'Local directory to clone/update (defaults to QUONFIG_DIR env var)',
31
31
  }),
32
+ rebase: Flags.boolean({
33
+ default: false,
34
+ description: 'Replay local commits on top of origin/main when the two have diverged. Conflicts are surfaced via standard git markers; resolve with `git rebase --continue`.',
35
+ }),
32
36
  workspace: Flags.string({
33
37
  description: 'Workspace ID (defaults to active profile)',
34
38
  }),
@@ -85,7 +89,43 @@ CLI shortcuts (no JSON editing needed for simple cases):
85
89
  const ff = await canFastForward(resolvedDir);
86
90
  const diverged = !ff && (await hasDivergedFromRemote(resolvedDir));
87
91
  if (diverged) {
88
- return this.err('Local commits diverge from remote — resolve manually.');
92
+ if (flags.rebase) {
93
+ this.log('Local and origin/main have diverged. Rebasing local commits on top of origin/main...');
94
+ const result = await gitPullRebase(resolvedDir);
95
+ if (result.kind === 'clean') {
96
+ this.log(`Rebased ${result.commitsRebased} local commit(s) onto origin/main. Run \`qfg push\` to publish.`);
97
+ // Fall through to post-pull work (QUONFIG_DIR write, pin backfill).
98
+ }
99
+ else if (result.kind === 'conflicts') {
100
+ this.log('');
101
+ this.log('Rebase paused with conflicts in:');
102
+ for (const f of result.conflictedFiles)
103
+ this.log(` ${f}`);
104
+ this.log('');
105
+ this.log('To finish the rebase:');
106
+ this.log(` 1. Edit each file above and pick the values you want; remove the git conflict markers.`);
107
+ this.log(` 2. git -C ${resolvedDir} add <files>`);
108
+ this.log(` 3. git -C ${resolvedDir} rebase --continue`);
109
+ this.log(` 4. qfg push`);
110
+ this.log('');
111
+ this.log('To abort the rebase and try a different approach:');
112
+ this.log(` git -C ${resolvedDir} rebase --abort`);
113
+ return this.err('Rebase paused on conflicts — see instructions above.');
114
+ }
115
+ else {
116
+ return this.err(`Rebase could not start: ${result.reason}\n\n` +
117
+ `Most likely the working tree has uncommitted edits. Commit or stash them, then re-run \`qfg pull --rebase\`.`);
118
+ }
119
+ }
120
+ else {
121
+ return this.err(`Local commits diverge from origin/main.\n\n` +
122
+ `Your local branch has commits that are not on origin, and origin has commits that are not local. ` +
123
+ `qfg pull will not merge automatically. Pick one:\n\n` +
124
+ ` Replay your local commits on top of origin/main:\n` +
125
+ ` qfg pull --rebase --dir ${resolvedDir}\n\n` +
126
+ ` Discard your local commits and take origin/main as-is (LOSES local work):\n` +
127
+ ` git -C ${resolvedDir} reset --hard origin/main`);
128
+ }
89
129
  }
90
130
  if (ff) {
91
131
  const newCommits = await gitMergeFfOnly(resolvedDir);
@@ -3,7 +3,7 @@ import * as path from 'node:path';
3
3
  import { Flags } from '@oclif/core';
4
4
  import { BaseCommand } from '../index.js';
5
5
  import { mintGiteaToken } from '../util/gitea-api.js';
6
- import { getRemoteUrl, gitFetch, gitSetRemote, isGitRepo, runGit } from '../util/git-ops.js';
6
+ import { dirtyTrackedFiles, getRemoteUrl, gitFetch, gitSetRemote, isGitRepo, isLocalBehindOrDivergedFromRemote, runGit } from '../util/git-ops.js';
7
7
  import { PushFatalError, runPush, } from '../push/run-push.js';
8
8
  import { computeBarePathDiff } from '../push/bare-path-diff.js';
9
9
  import { callConfigsPush } from '../push/config-push-client.js';
@@ -202,6 +202,29 @@ export function buildRealDeps(cmd, orgSlug) {
202
202
  const probe = await ensureBarePathProbe(dir);
203
203
  return probe.totalRemoteFiles;
204
204
  },
205
+ async isLocalBehindRemote(dir) {
206
+ // qfg-fboj: stale-HEAD guard for the clone path. Implementation
207
+ // lives in git-ops.ts so it can be unit-tested against a real
208
+ // local git repo. We still gate on isGitRepo here because the
209
+ // helper does not (it returns false on any error, but we want to
210
+ // skip the rev-parse cost on the bare path entirely).
211
+ if (!(await isGitRepo(dir)))
212
+ return false;
213
+ return isLocalBehindOrDivergedFromRemote(dir);
214
+ },
215
+ async dirtyTrackedFiles(dir) {
216
+ // qfg-fboj: surface uncommitted edits so the user sees they were
217
+ // not included in this push. Only meaningful on the clone path —
218
+ // bare path doesn't have a .git/ to status.
219
+ if (!(await isGitRepo(dir)))
220
+ return [];
221
+ try {
222
+ return await dirtyTrackedFiles(dir);
223
+ }
224
+ catch {
225
+ return [];
226
+ }
227
+ },
205
228
  };
206
229
  const deps = {
207
230
  async mintWriteToken(requestedTarget) {
@@ -1,18 +1,21 @@
1
1
  import { type ImportState } from './import-state.js';
2
2
  import { type MigrationReportData } from './migration-report.js';
3
3
  import { type PushIdentity } from '../util/clone-and-stack-push.js';
4
- import type { CoercedSentinelSummary, DroppedOverrideSummary, DuplicateResolutionSummary, LegacyChange, MigrationSource, SkippedConfigSummary } from './source.js';
4
+ import type { CoercedSentinelSummary, DroppedOverrideSummary, DuplicateResolution, DuplicateResolutionSummary, LegacyChange, MigrationSource, SkippedConfigSummary } from './source.js';
5
5
  export interface ApplyLocalMigrationOptions {
6
6
  /** Commit author. Defaults to the migrator identity. */
7
7
  author?: PushIdentity;
8
8
  /** Initial branch name when initializing a fresh repo. Defaults to `main`. */
9
9
  branch?: string;
10
10
  changes: LegacyChange[];
11
- commitMessage: string;
12
11
  /** Environments discovered from the source (slugified). Written to `quonfig.json` if missing. */
13
12
  environments: string[];
14
13
  importState: ImportState;
15
14
  localDir: string;
15
+ /**
16
+ * Base report data. The migrator overrides `counts` after writing files,
17
+ * since counts must reflect what was actually written to disk (qfg-7eig).
18
+ */
16
19
  reportData: MigrationReportData;
17
20
  source: MigrationSource;
18
21
  }
@@ -31,4 +34,18 @@ export interface ApplyLocalMigrationResult {
31
34
  /** Configs soft-skipped during translate due to invalid source data. Null if none. */
32
35
  skippedConfigs: SkippedConfigSummary | null;
33
36
  }
37
+ export declare class MigratorKeyCollisionError extends Error {
38
+ readonly destPath: string;
39
+ readonly firstSourceKey: string;
40
+ readonly secondSourceKey: string;
41
+ constructor(destPath: string, firstSourceKey: string, secondSourceKey: string);
42
+ }
43
+ export declare const writeQuonfigFiles: (dir: string, changes: LegacyChange[], source: MigrationSource) => WriteQuonfigFilesResult;
44
+ interface WriteQuonfigFilesResult {
45
+ livePaths: string[];
46
+ resolutions: DuplicateResolution[];
47
+ }
48
+ export declare const buildMigrationCounts: (livePaths: string[], environmentsMapped: number, itemsSkipped: number) => import("./migration-report.js").MigrationReportCounts;
49
+ export declare const buildMigrationCommitMessage: (source: string, counts: import("./migration-report.js").MigrationReportCounts) => string;
34
50
  export declare const applyLocalMigration: (opts: ApplyLocalMigrationOptions) => Promise<ApplyLocalMigrationResult>;
51
+ export {};