fg-devkit 1.21.5 → 1.23.1

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 (70) hide show
  1. package/README.md +16 -7
  2. package/configs/dependency-policy.vue-workspace.json +27 -27
  3. package/configs/eslint.config.base.d.ts +1 -0
  4. package/configs/eslint.config.base.js +3 -0
  5. package/configs/eslint.sync-stub.js +1 -0
  6. package/package.json +11 -81
  7. package/scaffold/vue/package.json +12 -12
  8. package/scripts/audit-override-commit.mjs +167 -24
  9. package/scripts/bump-node-toolchain.mjs +8 -31
  10. package/scripts/bump-pnpm-toolchain.mjs +8 -29
  11. package/scripts/check-dependency-policy.mjs +10 -35
  12. package/scripts/fg-devkit.mjs +274 -331
  13. package/scripts/fleet-bump-fg-devkit.mjs +4 -5
  14. package/scripts/fleet-git-commit-push-scoped.mjs +6 -26
  15. package/scripts/fleet-git-discard-scoped.mjs +5 -25
  16. package/scripts/fleet-git-pull-scoped.mjs +5 -25
  17. package/scripts/fleet-vulns.mjs +4 -9
  18. package/scripts/generate-release-notes.mjs +1 -1
  19. package/scripts/{audit-fix-plan.mjs → lib/audit-fix-plan.mjs} +77 -4
  20. package/scripts/{audit-fix-plan.test.mjs → lib/audit-fix-plan.test.mjs} +50 -2
  21. package/scripts/lib/common.mjs +95 -0
  22. package/scripts/{dependency-policy-toolchain.mjs → lib/dependency-policy-toolchain.mjs} +3 -35
  23. package/scripts/{git-commit-push-fleet-common.mjs → lib/git-commit-push-fleet-common.mjs} +1 -1
  24. package/scripts/{git-discard-fleet-common.mjs → lib/git-discard-fleet-common.mjs} +1 -1
  25. package/scripts/{git-pull-fleet-common.mjs → lib/git-pull-fleet-common.mjs} +1 -1
  26. package/scripts/{override-changes.mjs → lib/override-changes.mjs} +20 -0
  27. package/scripts/{piwik-dimensions.test.mjs → lib/piwik-dimensions.test.mjs} +0 -5
  28. package/scripts/{release-notes-common.mjs → lib/release-notes-common.mjs} +5 -7
  29. package/scripts/{resolve-dependency-policy-path.mjs → lib/resolve-dependency-policy-path.mjs} +4 -3
  30. package/scripts/piwik.mjs +6 -6
  31. package/scripts/poeditor-download.mjs +1 -1
  32. package/scripts/poeditor-upload.mjs +1 -1
  33. package/scripts/policy-outdated.mjs +3 -8
  34. package/scripts/policy-upgrade-commit.mjs +5 -26
  35. package/scripts/policy-upgrade.mjs +6 -30
  36. package/scripts/policy-vulns.mjs +3 -8
  37. package/scripts/prune-overrides.mjs +5 -16
  38. package/scripts/prune-release-age-exclude.mjs +1 -1
  39. package/scripts/refresh-dependency-policy-from-repos.mjs +5 -7
  40. package/scripts/release.sh +2 -1
  41. package/scripts/scaffold-vue.mjs +1 -3
  42. package/scripts/sync-consumer-lefthook.mjs +7 -24
  43. package/scripts/sync-core-coding-standards.mjs +2 -17
  44. package/scripts/sync-dependency-check-suppressions-commit.mjs +7 -46
  45. package/scripts/sync-dependency-policy-commit.mjs +6 -38
  46. package/scripts/sync-dependency-policy.mjs +9 -35
  47. package/scripts/sync-node-toolchain.mjs +5 -7
  48. package/scripts/sync-pnpm-toolchain.mjs +5 -7
  49. package/scripts/sync-scaffold-vue-deps.mjs +6 -7
  50. package/scripts/sync-tooling-configs.mjs +8 -23
  51. package/scripts/sync-vite-plugin-vue-inspector.mjs +7 -39
  52. package/scripts/verify-release-notes.mjs +1 -1
  53. package/scaffold/vue/.gitignore +0 -13
  54. package/scripts/cz/.cz-config.cjs +0 -29
  55. package/scripts/fleet-git-commit-push.mjs +0 -75
  56. package/scripts/fleet-git-discard.mjs +0 -66
  57. package/scripts/fleet-git-pull.mjs +0 -65
  58. package/scripts/sync-peers-from-dev.mjs +0 -31
  59. package/scripts/vuln-commit.mjs +0 -142
  60. /package/configs/{fallowrc.sync-stub.json → fallowrc.json} +0 -0
  61. /package/scripts/{dependency-policy-pins.mjs → lib/dependency-policy-pins.mjs} +0 -0
  62. /package/scripts/{fleet-project-repos.mjs → lib/fleet-project-repos.mjs} +0 -0
  63. /package/scripts/{fleet-repo-picker.mjs → lib/fleet-repo-picker.mjs} +0 -0
  64. /package/scripts/{fleet-vue-repos.mjs → lib/fleet-vue-repos.mjs} +0 -0
  65. /package/scripts/{git-fleet-common.mjs → lib/git-fleet-common.mjs} +0 -0
  66. /package/scripts/{node-toolchain-sync.mjs → lib/node-toolchain-sync.mjs} +0 -0
  67. /package/scripts/{piwik-common.mjs → lib/piwik-common.mjs} +0 -0
  68. /package/scripts/{piwik-dimensions.mjs → lib/piwik-dimensions.mjs} +0 -0
  69. /package/scripts/{pnpm-toolchain-sync.mjs → lib/pnpm-toolchain-sync.mjs} +0 -0
  70. /package/scripts/{poeditor-common.mjs → lib/poeditor-common.mjs} +0 -0
@@ -31,8 +31,7 @@
31
31
  * pnpm exec fg-devkit generate-release-notes [--tag=vX.Y.Z]
32
32
  * pnpm exec fg-devkit verify-release-notes
33
33
  * pnpm exec fg-devkit sync-release-notes-config
34
- * pnpm exec fg-devkit commit [cz-args...] (czg/cz-git with fg-devkit bundled config)
35
- * pnpm exec fg-devkit commit-vuln (staged package.json version bumps → vuln() commit)
34
+ * pnpm exec fg-devkit commit [git-commit-args...] (conventional commit prompts, then git commit)
36
35
  * pnpm exec fg-devkit lint [eslint-args...] (run ESLint with fg-devkit bundled packages)
37
36
  * pnpm exec fg-devkit lint-fix [eslint-args...] (same as lint plus --fix)
38
37
  * pnpm exec fg-devkit format [prettier-args...] (run Prettier with fg-devkit bundled package)
@@ -57,10 +56,11 @@ import fs from 'node:fs'
57
56
  import path from 'node:path'
58
57
  import { fileURLToPath } from 'node:url'
59
58
  import { createRequire } from 'node:module'
59
+ import { styleText } from 'node:util'
60
+ import { fgDevkitRoot } from './lib/common.mjs'
60
61
 
61
62
  const scriptDir = path.dirname(fileURLToPath(import.meta.url))
62
63
  const require = createRequire(import.meta.url)
63
- const fgRoot = path.resolve(scriptDir, '..')
64
64
 
65
65
  /** Run another script in this package via Node; inherits stdio and returns the child exit code. */
66
66
  function runNode(scriptRelative, args = [], options = {}) {
@@ -81,6 +81,18 @@ function runNode(scriptRelative, args = [], options = {}) {
81
81
  return r.status ?? 1
82
82
  }
83
83
 
84
+ /**
85
+ * Menu label with dimmed tags: `Upgrade packages [interactive, auto-commit]`.
86
+ * interactive: the action stops and asks for input while it runs. autoCommit: it commits on its own.
87
+ */
88
+ function tagged(name, { interactive = false, autoCommit = false }) {
89
+ const tags = [interactive && 'interactive', autoCommit && 'auto-commit'].filter(Boolean)
90
+ return `${name} ${styleText('dim', `[${tags.join(', ')}]`)}`
91
+ }
92
+
93
+ const autoCommit = (name) => tagged(name, { autoCommit: true })
94
+ const interactive = (name) => tagged(name, { interactive: true })
95
+
84
96
  /**
85
97
  * Run release.sh (pnpm version bump, git push, gh release). Uses consumer cwd.
86
98
  * Args: [release|candidate] [patch|minor|major] or [patch|minor|major] [release|candidate].
@@ -149,7 +161,7 @@ function runSetupLefthook() {
149
161
  /**
150
162
  * Full consumer setup flow:
151
163
  * - Sync required consumer scripts for fgd commands
152
- * - Setup lefthook and migrate from husky/lint-staged
164
+ * - Setup lefthook
153
165
  * - Step 3: sync project configs
154
166
  * - Step 2: sync dependency policy
155
167
  * - Then run pnpm install
@@ -184,7 +196,7 @@ function runMaintainerAuditAll() {
184
196
  * Prompt for a conventional commit message with fg-devkit's legacy question flow,
185
197
  * then run `git commit` directly.
186
198
  */
187
- async function runCzg(args = [], extraEnv = {}) {
199
+ async function runCommit(args = [], extraEnv = {}) {
188
200
  let selectPrompt
189
201
  let inputPrompt
190
202
  try {
@@ -395,12 +407,18 @@ function runBundledTypeScript(args = []) {
395
407
  return r.status ?? 1
396
408
  }
397
409
 
398
- /** Run Fallow from fg-devkit's bundled dependency set. */
410
+ const FALLOW_CONFIG_FILES = ['.fallowrc.json', '.fallowrc.jsonc', 'fallow.toml', '.fallow.toml']
411
+
412
+ /**
413
+ * Run Fallow from fg-devkit's bundled dependency set. Uses fg-devkit's configs/fallowrc.json
414
+ * unless the repo has its own Fallow config or the caller passes -c/--config, so config
415
+ * fixes reach every repo on the next fg-devkit bump.
416
+ */
399
417
  function runBundledFallow(args = []) {
400
418
  let fallowBin
401
419
  try {
402
420
  const fallowPkgJson = require.resolve('fallow/package.json', {
403
- paths: [fgRoot, process.cwd()],
421
+ paths: [fgDevkitRoot, process.cwd()],
404
422
  })
405
423
  const fallowPkgRoot = path.dirname(fallowPkgJson)
406
424
  fallowBin = path.join(fallowPkgRoot, 'bin', 'fallow')
@@ -413,7 +431,12 @@ function runBundledFallow(args = []) {
413
431
  return 1
414
432
  }
415
433
 
416
- const r = spawnSync(process.execPath, [fallowBin, ...args], {
434
+ const hasConfig =
435
+ args.some((arg) => arg === '-c' || arg === '--config' || arg.startsWith('--config=')) ||
436
+ FALLOW_CONFIG_FILES.some((name) => fs.existsSync(path.join(process.cwd(), name)))
437
+ const configArgs = hasConfig ? [] : ['--config', path.join(fgDevkitRoot, 'configs', 'fallowrc.json')]
438
+
439
+ const r = spawnSync(process.execPath, [fallowBin, ...args, ...configArgs], {
417
440
  stdio: 'inherit',
418
441
  cwd: process.cwd(),
419
442
  env: process.env,
@@ -427,7 +450,7 @@ function runBundledFallow(args = []) {
427
450
  }
428
451
 
429
452
  function syncFileFromTemplate(relativeSourcePath, targetPath) {
430
- const sourcePath = path.join(fgRoot, relativeSourcePath)
453
+ const sourcePath = path.join(fgDevkitRoot, relativeSourcePath)
431
454
  if (!fs.existsSync(sourcePath)) {
432
455
  console.error(`[fg-devkit] Missing template: ${sourcePath}`)
433
456
  return 1
@@ -447,7 +470,6 @@ function runSyncConfigTarget(target) {
447
470
  if (target === 'eslint') return syncFileFromTemplate('configs/eslint.sync-stub.js', 'eslint.config.js')
448
471
  if (target === 'prettier') return syncFileFromTemplate('configs/prettier.sync-stub.js', 'prettier.config.js')
449
472
  if (target === 'tsconfig') return syncFileFromTemplate('configs/tsconfig.app.sync.json', 'tsconfig.json')
450
- if (target === 'fallow') return syncFileFromTemplate('configs/fallowrc.sync-stub.json', '.fallowrc.json')
451
473
  if (target === 'dependency-check') return runNode('sync-dependency-check-suppressions-commit.mjs')
452
474
  if (target === 'cursor') return runNode('sync-core-coding-standards.mjs')
453
475
  if (target === 'vue-inspector') return runNode('sync-vite-plugin-vue-inspector.mjs')
@@ -470,11 +492,8 @@ const DIRECT_COMMANDS = {
470
492
  'sync-node-toolchain': 'sync-node-toolchain.mjs',
471
493
  'sync-pnpm-toolchain': 'sync-pnpm-toolchain.mjs',
472
494
  'bump-node-toolchain': 'bump-node-toolchain.mjs',
473
- 'fleet-git-pull': 'fleet-git-pull.mjs',
474
495
  'fleet-git-pull-scoped': 'fleet-git-pull-scoped.mjs',
475
- 'fleet-git-commit-push': 'fleet-git-commit-push.mjs',
476
496
  'fleet-git-commit-push-scoped': 'fleet-git-commit-push-scoped.mjs',
477
- 'fleet-git-discard': 'fleet-git-discard.mjs',
478
497
  'fleet-git-discard-scoped': 'fleet-git-discard-scoped.mjs',
479
498
  'policy-upgrade': 'policy-upgrade.mjs',
480
499
  'policy-upgrade-commit': 'policy-upgrade-commit.mjs',
@@ -524,18 +543,12 @@ Commands:
524
543
  bump-node-toolchain [version] [projects-root] [options]
525
544
  Maintainer: set policy.node and sync Node toolchain across fleet
526
545
  Options: --dry-run, --skip-fleet, --no-engines, --engines=RANGE, --pick, --all, --only=a,b
527
- fleet-git-pull [projects-root] [--dry-run]
528
- Maintainer: git pull (current branch) for all git repos under projects root
529
546
  fleet-git-pull-scoped [projects-root] [options]
530
547
  Maintainer: git pull (current branch) for chosen repositories
531
548
  Options: --dry-run, --fg-devkit-only, --pick, --all, --only=a,b
532
- fleet-git-commit-push --message="..." [projects-root] [--dry-run]
533
- Maintainer: stage, commit, and push all git repos under projects root
534
549
  fleet-git-commit-push-scoped --message="..." [projects-root] [options]
535
550
  Maintainer: stage, commit, and push chosen repositories
536
551
  Options: --dry-run, --fg-devkit-only, --pick, --all, --only=a,b
537
- fleet-git-discard [projects-root] [--dry-run]
538
- Maintainer: discard tracked git changes in all git repos under projects root
539
552
  fleet-git-discard-scoped [projects-root] [options]
540
553
  Maintainer: discard tracked git changes in chosen repositories
541
554
  Options: --dry-run, --fg-devkit-only, --pick, --all, --only=a,b
@@ -546,6 +559,7 @@ Commands:
546
559
  --prune-overrides also drops unused workspace overrides in a second commit
547
560
  audit-override-commit [--no-verify] [--no-commit]
548
561
  Fix pnpm audit findings: refresh lockfile first, override only when a range blocks the fix, verify, auto-commit
562
+ Asks before forcing the rest; --allow-major, --allow-young, --bump-direct say yes without asking
549
563
  prune-overrides [--fix] [--commit]
550
564
  Report unused workspace overrides; --fix removes them; --commit also auto-commits
551
565
  prune-release-age-exclude [--fix] [--commit]
@@ -558,7 +572,7 @@ Commands:
558
572
  sync-core-coding-standards Copy .cursor core coding standards with consumer package name
559
573
  sync-vite-plugin-vue-inspector Add/pin vite-plugin-vue-inspector, wire vite.config, install, commit
560
574
  sync-consumer-scripts Enforce standard fgd scripts in consumer package.json
561
- setup-lefthook Setup lefthook and migrate from husky/lint-staged
575
+ setup-lefthook Write lefthook.yml and install the git hooks
562
576
  full-consumer-setup Run: scripts -> lefthook -> step 3 -> step 2 -> pnpm install
563
577
  create-release [release|candidate] [patch|minor|major] [--skip-release-notes]
564
578
  Stable release or candidate (X.Y.Z-rc.N); omit = interactive menu
@@ -567,8 +581,7 @@ Commands:
567
581
  Generate CHANGELOG.md and user changelog via git-cliff
568
582
  verify-release-notes Interactive review step before committing release notes
569
583
  sync-release-notes-config Add CHANGELOG.user.md and generate:release-notes script when missing
570
- commit [args...] czg/cz-git with fg-devkit bundled config
571
- commit-vuln Commit staged dependency bumps as vuln() (alias: vuln-commit)
584
+ commit [args...] Conventional commit prompts, then git commit [args...]
572
585
  lint [eslint-args...] Run ESLint using fg-devkit bundled ESLint stack
573
586
  lint-fix [eslint-args...] Run ESLint with --fix using fg-devkit bundled ESLint stack
574
587
  format [prettier-args...] Run Prettier using fg-devkit bundled package
@@ -592,7 +605,7 @@ Options:
592
605
  }
593
606
 
594
607
  function isFgDevkitMaintainerContext() {
595
- if (path.resolve(process.cwd()) !== fgRoot) {
608
+ if (path.resolve(process.cwd()) !== fgDevkitRoot) {
596
609
  return false
597
610
  }
598
611
  const pkgPath = path.join(process.cwd(), 'package.json')
@@ -644,52 +657,38 @@ async function runInteractiveMenu() {
644
657
  const isMaintainer = isFgDevkitMaintainerContext()
645
658
  const groups = [
646
659
  {
647
- name: '1) Create commit',
660
+ name: interactive('Commit'),
648
661
  value: 'commit',
662
+ description: 'Write a conventional commit message step by step and commit the staged changes',
649
663
  },
664
+ { name: 'Release', value: 'release', description: 'Bump the version, tag it and publish a GitHub release' },
665
+ { name: 'Lint & typecheck', value: 'lint-typecheck', description: 'Run ESLint and TypeScript' },
666
+ { name: 'Translations', value: 'poeditor', description: 'Download or upload translations in POEditor' },
650
667
  {
651
- name: '2) Create release',
652
- value: 'release',
653
- },
654
- {
655
- name: '3) Lint and typecheck',
656
- value: 'lint-typecheck',
657
- },
658
- {
659
- name: '4) Poeditor',
660
- value: 'poeditor',
661
- },
662
- {
663
- name: '5) Dependecy policy',
668
+ name: 'Dependency policy',
664
669
  value: 'dependency-policy',
670
+ description: 'Keep package.json in line with the shared policy and fix vulnerabilities',
665
671
  },
666
672
  {
667
- name: '6) Sync configs',
673
+ name: 'Sync configs',
668
674
  value: 'sync-configs',
675
+ description: 'Overwrite shared config files with the versions in fg-devkit',
669
676
  },
677
+ { name: 'Setup', value: 'setup', description: 'Git hooks and first-time setup of a repo' },
670
678
  {
671
- name: '7) Setup',
672
- value: 'setup',
673
- },
674
- {
675
- name: '8) Fallow analysis',
676
- value: 'fallow-analysis',
677
- },
678
- {
679
- name: '9) Piwik',
680
- value: 'piwik',
679
+ name: 'Dead code (Fallow)',
680
+ value: 'fallow-dead-code',
681
+ description: 'Find unused files, exports and dependencies',
681
682
  },
683
+ { name: 'Piwik', value: 'piwik', description: 'Manage Piwik PRO custom dimensions' },
682
684
  ]
683
- if (isMaintainer) {
684
- groups.push({
685
- name: '10) Maintainer',
685
+ if (isMaintainer) groups.push({
686
+ name: 'Maintainer',
686
687
  value: 'maintainer',
688
+ description: 'Dependency policy, toolchain and git across all repos',
687
689
  })
688
- }
689
- groups.push({
690
- name: isMaintainer ? '11) Exit' : '10) Exit',
691
- value: 'exit',
692
- })
690
+ groups.push({ name: 'Exit', value: 'exit' })
691
+ for (const [index, group] of groups.entries()) group.name = `${index + 1}) ${group.name}`
693
692
 
694
693
  while (true) {
695
694
  const group = await selectPrompt({
@@ -700,26 +699,15 @@ async function runInteractiveMenu() {
700
699
  if (group === 'exit') return 0
701
700
 
702
701
  if (group === 'commit') {
703
- const choice = await selectPrompt({
704
- message: 'Create commit',
705
- choices: [
706
- { name: 'Commit', value: 'commit' },
707
- { name: 'Commit vulnerability', value: 'commit-vuln' },
708
- { name: 'Cancel', value: 'cancel' },
709
- ],
710
- default: 'commit',
711
- loop: false,
712
- })
713
- if (choice === 'cancel') continue
714
- return runSelectedAction(() => (choice === 'commit' ? runCzg() : runNode('vuln-commit.mjs')))
702
+ return runSelectedAction(() => runCommit())
715
703
  }
716
704
 
717
705
  if (group === 'release') {
718
706
  const releaseKind = await selectPrompt({
719
- message: 'Create release',
707
+ message: 'Release',
720
708
  choices: [
721
- { name: 'Release', value: 'release' },
722
- { name: 'Candidate', value: 'candidate' },
709
+ { name: 'Release', value: 'release', description: 'Tag the next version and publish a GitHub release' },
710
+ { name: 'Release candidate', value: 'candidate', description: 'Tag a prerelease such as 2.8.0-rc.1' },
723
711
  { name: 'Cancel', value: 'cancel' },
724
712
  ],
725
713
  default: 'release',
@@ -728,10 +716,14 @@ async function runInteractiveMenu() {
728
716
  if (releaseKind === 'cancel') continue
729
717
 
730
718
  const releaseMode = await selectPrompt({
731
- message: 'Create release',
719
+ message: 'Release notes',
732
720
  choices: [
733
- { name: 'Release', value: 'without-notes' },
734
- { name: 'Release + release notes', value: 'with-notes' },
721
+ { name: 'Without notes', value: 'without-notes', description: 'GitHub generates the release notes' },
722
+ {
723
+ name: interactive('With notes'),
724
+ value: 'with-notes',
725
+ description: 'Generate CHANGELOG.md and CHANGELOG.user.md with git-cliff and review them before release',
726
+ },
735
727
  { name: 'Cancel', value: 'cancel' },
736
728
  ],
737
729
  default: 'without-notes',
@@ -740,11 +732,11 @@ async function runInteractiveMenu() {
740
732
  if (releaseMode === 'cancel') continue
741
733
 
742
734
  const bumpChoice = await selectPrompt({
743
- message: releaseKind === 'candidate' ? 'Candidate' : 'Release',
735
+ message: releaseKind === 'candidate' ? 'Release candidate' : 'Release',
744
736
  choices: [
745
- { name: 'Patch', value: 'patch' },
746
- { name: 'Minor', value: 'minor' },
747
- { name: 'Major', value: 'major' },
737
+ { name: 'Patch', value: 'patch', description: 'Bug fixes only: 1.2.3 → 1.2.4' },
738
+ { name: 'Minor', value: 'minor', description: 'New features, backwards compatible: 1.2.3 → 1.3.0' },
739
+ { name: 'Major', value: 'major', description: 'Breaking changes: 1.2.3 → 2.0.0' },
748
740
  { name: 'Cancel', value: 'cancel' },
749
741
  ],
750
742
  default: 'patch',
@@ -764,11 +756,11 @@ async function runInteractiveMenu() {
764
756
 
765
757
  if (group === 'lint-typecheck') {
766
758
  const choice = await selectPrompt({
767
- message: 'Lint and typecheck',
759
+ message: 'Lint & typecheck',
768
760
  choices: [
769
- { name: 'Lint and typecheck', value: 'both' },
770
- { name: 'Lint', value: 'lint' },
771
- { name: 'Typecheck', value: 'typecheck' },
761
+ { name: 'Lint & typecheck', value: 'both', description: 'ESLint on the whole repo, then tsc --noEmit' },
762
+ { name: 'Lint', value: 'lint', description: 'ESLint on the whole repo' },
763
+ { name: 'Typecheck', value: 'typecheck', description: 'tsc --noEmit with tsconfig.json' },
772
764
  { name: 'Cancel', value: 'cancel' },
773
765
  ],
774
766
  default: 'both',
@@ -786,10 +778,10 @@ async function runInteractiveMenu() {
786
778
 
787
779
  if (group === 'poeditor') {
788
780
  const choice = await selectPrompt({
789
- message: 'Poeditor',
781
+ message: 'Translations',
790
782
  choices: [
791
- { name: 'Download', value: 'download' },
792
- { name: 'Upload', value: 'upload' },
783
+ { name: 'Download', value: 'download', description: 'Download translations from POEditor into the repo' },
784
+ { name: 'Upload', value: 'upload', description: 'Upload the repo\'s translations to POEditor' },
793
785
  { name: 'Cancel', value: 'cancel' },
794
786
  ],
795
787
  default: 'download',
@@ -801,12 +793,13 @@ async function runInteractiveMenu() {
801
793
  }
802
794
 
803
795
  const uploadMode = await selectPrompt({
804
- message: 'Upload mode',
796
+ message: 'Upload',
805
797
  choices: [
806
- { name: 'Upload (keep terms missing from file)', value: 'upload' },
798
+ { name: 'Upload', value: 'upload', description: 'Terms missing from the file stay in POEditor' },
807
799
  {
808
- name: 'Upload + sync terms (delete terms missing from file)',
800
+ name: 'Upload and delete missing terms',
809
801
  value: 'upload-sync',
802
+ description: 'Terms missing from the file are deleted in POEditor, with all their translations',
810
803
  },
811
804
  { name: 'Cancel', value: 'cancel' },
812
805
  ],
@@ -830,14 +823,38 @@ async function runInteractiveMenu() {
830
823
  const choice = await selectPrompt({
831
824
  message: 'Piwik PRO custom dimensions',
832
825
  choices: [
833
- { name: 'List dimensions in Piwik', value: 'list' },
834
- { name: 'List local dimension map', value: 'list-local' },
835
- { name: 'Compare local map with Piwik', value: 'diff' },
836
- { name: 'Add missing dimensions', value: 'add' },
837
- { name: 'Update dimension names', value: 'update' },
838
- { name: 'Sync from one side to another', value: 'sync' },
839
- { name: 'Compare environments', value: 'compare' },
840
- { name: 'Migrate between environments', value: 'migrate' },
826
+ { name: 'List in Piwik', value: 'list', description: 'Dimensions on a site from src/config/env.ts' },
827
+ {
828
+ name: 'List local',
829
+ value: 'list-local',
830
+ description: 'Slot map in dimensions.ts or piwikDimensions.ts. Does not call Piwik',
831
+ },
832
+ {
833
+ name: 'Compare local with Piwik',
834
+ value: 'diff',
835
+ description: 'Show where the local slot map and Piwik differ',
836
+ },
837
+ {
838
+ name: 'Add missing dimensions',
839
+ value: 'add',
840
+ description: 'Create local dimensions that are missing in Piwik, one slot at a time',
841
+ },
842
+ {
843
+ name: 'Update dimension names',
844
+ value: 'update',
845
+ description: 'Rename Piwik dimensions to the local names',
846
+ },
847
+ {
848
+ name: 'Sync',
849
+ value: 'sync',
850
+ description: 'Copy dimensions between the local map and a site, or between two sites',
851
+ },
852
+ { name: 'Compare environments', value: 'compare', description: 'Compare the dimensions on two Piwik sites' },
853
+ {
854
+ name: 'Migrate between environments',
855
+ value: 'migrate',
856
+ description: 'Copy dimensions from one Piwik site to another',
857
+ },
841
858
  { name: 'Cancel', value: 'cancel' },
842
859
  ],
843
860
  default: 'diff',
@@ -848,7 +865,7 @@ async function runInteractiveMenu() {
848
865
  if (choice === 'list-local') return runSelectedAction(() => runNode('piwik.mjs', ['list', '--local']))
849
866
  if (choice === 'diff') return runSelectedAction(() => runNode('piwik.mjs', ['diff']))
850
867
  if (choice === 'compare' || choice === 'migrate') {
851
- const { defaultEnvironmentPair, listPiwikEnvironments } = await import('./piwik-common.mjs')
868
+ const { defaultEnvironmentPair, listPiwikEnvironments } = await import('./lib/piwik-common.mjs')
852
869
  const environments = listPiwikEnvironments(process.cwd())
853
870
  if (environments.length < 2) {
854
871
  console.log('[piwik] Need two Piwik sites in src/config/env.ts.')
@@ -874,8 +891,8 @@ async function runInteractiveMenu() {
874
891
  const writeMode = await selectPrompt({
875
892
  message: `Write dimensions from ${from} to ${to}?`,
876
893
  choices: [
877
- { name: 'Dry run', value: 'dry-run' },
878
- { name: 'Apply', value: 'apply' },
894
+ { name: 'Dry run', value: 'dry-run', description: 'Show what would change without writing anything' },
895
+ { name: 'Apply', value: 'apply', description: 'Write the changes' },
879
896
  { name: 'Cancel', value: 'cancel' },
880
897
  ],
881
898
  default: 'dry-run',
@@ -895,10 +912,10 @@ async function runInteractiveMenu() {
895
912
  return runSelectedAction(() => runNode('piwik.mjs', migrateArgs))
896
913
  }
897
914
  if (choice === 'sync') {
898
- const { listPiwikEnvironments } = await import('./piwik-common.mjs')
915
+ const { listPiwikEnvironments } = await import('./lib/piwik-common.mjs')
899
916
  const environments = listPiwikEnvironments(process.cwd())
900
917
  const endpoints = [
901
- { name: 'Local map', value: 'local' },
918
+ { name: 'Local map', value: 'local', description: 'dimensions.ts or piwikDimensions.ts in this repo' },
902
919
  ...environments.map((site) => ({ name: site.environment, value: site.environment })),
903
920
  ]
904
921
  const from = await selectPrompt({
@@ -922,8 +939,8 @@ async function runInteractiveMenu() {
922
939
  const writeMode = await selectPrompt({
923
940
  message: from === 'local' ? `Write the local map up to ${to}?` : to === 'local' ? `Write tracking ids from ${from} into the local map?` : `Write dimensions from ${from} to ${to}?`,
924
941
  choices: [
925
- { name: 'Dry run', value: 'dry-run' },
926
- { name: 'Apply', value: 'apply' },
942
+ { name: 'Dry run', value: 'dry-run', description: 'Show what would change without writing anything' },
943
+ { name: 'Apply', value: 'apply', description: 'Write the changes' },
927
944
  { name: 'Cancel', value: 'cancel' },
928
945
  ],
929
946
  default: 'dry-run',
@@ -950,8 +967,8 @@ async function runInteractiveMenu() {
950
967
  const writeMode = await selectPrompt({
951
968
  message: 'Write to Piwik?',
952
969
  choices: [
953
- { name: 'Dry run', value: 'dry-run' },
954
- { name: 'Apply', value: 'apply' },
970
+ { name: 'Dry run', value: 'dry-run', description: 'Show what would change without writing anything' },
971
+ { name: 'Apply', value: 'apply', description: 'Write the changes' },
955
972
  { name: 'Cancel', value: 'cancel' },
956
973
  ],
957
974
  default: 'dry-run',
@@ -973,20 +990,38 @@ async function runInteractiveMenu() {
973
990
 
974
991
  if (group === 'dependency-policy') {
975
992
  const choice = await selectPrompt({
976
- message: 'Dependecy policy',
993
+ message: 'Dependency policy',
977
994
  choices: [
978
- { name: 'Check', value: 'check' },
979
- { name: 'Sync and auto-commit', value: 'sync-commit' },
980
- { name: 'Upgrade package.json packages and auto-commit', value: 'upgrade-commit' },
981
- { name: 'Upgrade package.json packages, prune overrides, and auto-commit', value: 'upgrade-commit-prune' },
982
995
  {
983
- name: 'Fix vulnerabilities (refresh, override, prune) and auto-commit',
996
+ name: 'Check',
997
+ value: 'check',
998
+ description: 'Check package.json against the dependency policy without changing anything',
999
+ },
1000
+ {
1001
+ name: autoCommit('Sync'),
1002
+ value: 'sync-commit',
1003
+ description: 'Set package.json versions to the dependency policy and run pnpm install',
1004
+ },
1005
+ {
1006
+ name: tagged('Upgrade packages', { interactive: true, autoCommit: true }),
1007
+ value: 'upgrade-commit-prune',
1008
+ description: 'Pick packages to upgrade with ncu, then prune unused overrides in a second commit',
1009
+ },
1010
+ {
1011
+ name: tagged('Fix vulnerabilities', { interactive: true, autoCommit: true }),
984
1012
  value: 'audit-override-commit',
1013
+ description:
1014
+ 'Refresh the lockfile, override where ranges block, prune, verify. Asks before forcing a new major, a young release or a direct bump',
1015
+ },
1016
+ {
1017
+ name: autoCommit('Prune overrides'),
1018
+ value: 'prune-overrides',
1019
+ description: 'Remove workspace overrides that no longer change pnpm audit',
985
1020
  },
986
- { name: 'Prune unused vulnerability overrides and auto-commit', value: 'prune-overrides' },
987
1021
  {
988
- name: 'Prune unneeded minimumReleaseAgeExclude entries and auto-commit',
1022
+ name: autoCommit('Prune minimumReleaseAgeExclude'),
989
1023
  value: 'prune-release-age-exclude',
1024
+ description: 'Remove pinned versions that are old enough or no longer in the lockfile',
990
1025
  },
991
1026
  { name: 'Cancel', value: 'cancel' },
992
1027
  ],
@@ -998,9 +1033,6 @@ async function runInteractiveMenu() {
998
1033
  if (choice === 'sync-commit') {
999
1034
  return runSelectedAction(() => runNode('sync-dependency-policy-commit.mjs'))
1000
1035
  }
1001
- if (choice === 'upgrade-commit') {
1002
- return runSelectedAction(() => runNode('policy-upgrade-commit.mjs'))
1003
- }
1004
1036
  if (choice === 'upgrade-commit-prune') {
1005
1037
  return runSelectedAction(() => runNode('policy-upgrade-commit.mjs', ['--prune-overrides']))
1006
1038
  }
@@ -1020,15 +1052,22 @@ async function runInteractiveMenu() {
1020
1052
  const choice = await selectPrompt({
1021
1053
  message: 'Sync configs',
1022
1054
  choices: [
1023
- { name: 'vscode', value: 'vscode' },
1024
- { name: 'eslint', value: 'eslint' },
1025
- { name: 'prettier', value: 'prettier' },
1026
- { name: 'tsconfig', value: 'tsconfig' },
1027
- { name: 'Fallow', value: 'fallow' },
1028
- { name: 'Dependency-Check suppressions (sync + commit)', value: 'dependency-check' },
1029
- { name: 'Cursor', value: 'cursor' },
1030
- { name: 'Vue inspector (vite-plugin-vue-inspector)', value: 'vue-inspector' },
1031
- { name: 'All', value: 'all' },
1055
+ { name: 'VS Code', value: 'vscode', description: 'Overwrite .vscode/settings.json with the shared settings' },
1056
+ { name: 'ESLint', value: 'eslint', description: 'Overwrite eslint.config.js with the shared config' },
1057
+ { name: 'Prettier', value: 'prettier', description: 'Overwrite prettier.config.js with the shared config' },
1058
+ { name: 'tsconfig', value: 'tsconfig', description: 'Overwrite tsconfig.json with the shared config' },
1059
+ {
1060
+ name: autoCommit('Dependency-Check suppressions'),
1061
+ value: 'dependency-check',
1062
+ description: 'Write the suppressions that match pnpm audit findings',
1063
+ },
1064
+ { name: 'Cursor rules', value: 'cursor', description: 'Write .cursor/rules/core-coding-standards.mdc' },
1065
+ {
1066
+ name: autoCommit('Vue inspector'),
1067
+ value: 'vue-inspector',
1068
+ description: 'Add vite-plugin-vue-inspector, wire it in vite.config and run pnpm install',
1069
+ },
1070
+ { name: 'All configs', value: 'all', description: 'VS Code, ESLint, Prettier and tsconfig' },
1032
1071
  { name: 'Cancel', value: 'cancel' },
1033
1072
  ],
1034
1073
  default: 'all',
@@ -1042,8 +1081,16 @@ async function runInteractiveMenu() {
1042
1081
  const choice = await selectPrompt({
1043
1082
  message: 'Setup',
1044
1083
  choices: [
1045
- { name: 'Lefthook', value: 'lefthook' },
1046
- { name: 'Full consumer setup', value: 'full-consumer-setup' },
1084
+ {
1085
+ name: 'Lefthook',
1086
+ value: 'lefthook',
1087
+ description: 'Write lefthook.yml and install the git hooks',
1088
+ },
1089
+ {
1090
+ name: 'Full setup',
1091
+ value: 'full-consumer-setup',
1092
+ description: 'Sync scripts, Lefthook, configs and dependency policy, then pnpm install',
1093
+ },
1047
1094
  { name: 'Cancel', value: 'cancel' },
1048
1095
  ],
1049
1096
  default: 'lefthook',
@@ -1053,225 +1100,128 @@ async function runInteractiveMenu() {
1053
1100
  return runSelectedAction(() => (choice === 'lefthook' ? runSetupLefthook() : runFullConsumerSetup()))
1054
1101
  }
1055
1102
 
1056
- if (group === 'fallow-analysis') {
1057
- const choice = await selectPrompt({
1058
- message: 'Fallow analysis',
1059
- choices: [
1060
- { name: 'dead code', value: 'dead-code' },
1061
- { name: 'Cancel', value: 'cancel' },
1062
- ],
1063
- default: 'dead-code',
1064
- loop: false,
1065
- })
1066
- if (choice === 'cancel') continue
1067
- return runSelectedAction(() => runBundledFallow([choice]))
1103
+ if (group === 'fallow-dead-code') {
1104
+ return runSelectedAction(() => runBundledFallow(['dead-code']))
1068
1105
  }
1069
1106
 
1070
1107
  if (group === 'maintainer') {
1071
1108
  const choice = await selectPrompt({
1072
1109
  message: 'Maintainer',
1073
1110
  choices: [
1074
- { name: 'Show outdated dependency policy packages', value: 'm1' },
1075
- { name: 'Upgrade dependency policy (interactive) and auto-commit', value: 'm2' },
1076
- { name: 'Refresh dependency policy from repos (and scaffold/vue package.json)', value: 'm3' },
1077
- { name: 'Check policy vulnerabilities (audit)', value: 'm4' },
1078
- { name: 'Check fg-devkit repo vulnerabilities', value: 'm5' },
1079
- { name: 'Check vulnerabilities across all Vue repos', value: 'm6' },
1080
- { name: 'Bump fg-devkit in all Vue repos, run sync-deps, and pnpm install', value: 'm7' },
1081
- { name: 'Bump pnpm toolchain (latest)', value: 'm8' },
1082
- { name: 'Bump Node toolchain (fleet sync)', value: 'm9' },
1083
- { name: 'Git pull all repositories', value: 'm10' },
1084
- { name: 'Git pull (choose repositories)', value: 'm11' },
1085
- { name: 'Stage, commit and push all repositories', value: 'm12' },
1086
- { name: 'Stage, commit and push (choose repositories)', value: 'm13' },
1087
- { name: 'Discard tracked changes in all repositories', value: 'm14' },
1088
- { name: 'Discard tracked changes (choose repositories)', value: 'm15' },
1111
+ {
1112
+ name: 'Outdated policy packages',
1113
+ value: 'policy-outdated',
1114
+ description: 'Policy packages that have newer versions on npm',
1115
+ },
1116
+ {
1117
+ name: tagged('Upgrade policy', { interactive: true, autoCommit: true }),
1118
+ value: 'policy-upgrade',
1119
+ description: 'Pick policy versions to upgrade with ncu',
1120
+ },
1121
+ {
1122
+ name: 'Refresh policy from repos',
1123
+ value: 'refresh-policy',
1124
+ description: 'Rebuild the policy from the packages used in Vue repos, and update scaffold/vue/package.json',
1125
+ },
1126
+ { name: 'Policy vulnerabilities', value: 'policy-vulns', description: 'pnpm audit of the versions in the policy' },
1127
+ { name: 'Vulnerabilities', value: 'vulns', description: 'pnpm audit across the Vue repos or in fg-devkit' },
1128
+ {
1129
+ name: interactive('Bump fg-devkit in Vue repos'),
1130
+ value: 'bump-fg-devkit',
1131
+ description: 'Bump fg-devkit to latest, sync the dependency policy and run pnpm install in each repo',
1132
+ },
1133
+ { name: 'Bump pnpm', value: 'bump-pnpm', description: 'Latest pnpm in the policy, scaffold and repos' },
1134
+ { name: 'Bump Node', value: 'bump-node', description: 'Sync the Node version from the policy to the repos' },
1135
+ { name: 'Git pull', value: 'git-pull', description: 'Pull the current branch in each repo' },
1136
+ {
1137
+ name: interactive('Commit & push'),
1138
+ value: 'git-commit-push',
1139
+ description: 'Stage all changes, commit and push in each repo',
1140
+ },
1141
+ { name: 'Discard changes', value: 'git-discard', description: 'Throw away uncommitted changes to tracked files in each repo' },
1089
1142
  { name: 'Cancel', value: 'cancel' },
1090
1143
  ],
1091
- default: 'm1',
1144
+ default: 'policy-outdated',
1092
1145
  loop: false,
1093
1146
  })
1094
1147
  if (choice === 'cancel') continue
1095
1148
  const maintainerCode = maintainerOnly()
1096
1149
  if (maintainerCode !== 0) return maintainerCode
1097
- if (choice === 'm1') return runSelectedAction(() => runNode('policy-outdated.mjs'))
1098
- if (choice === 'm2') return runSelectedAction(() => runNode('policy-upgrade.mjs'))
1099
- if (choice === 'm3') return runSelectedAction(() => runNode('refresh-dependency-policy-from-repos.mjs'))
1100
- if (choice === 'm4') return runSelectedAction(() => runNode('policy-vulns.mjs'))
1101
- if (choice === 'm5') return runSelectedAction(() => runMaintainerAuditAll())
1102
- if (choice === 'm6') return runSelectedAction(() => runNode('fleet-vulns.mjs'))
1103
- if (choice === 'm7') return runSelectedAction(() => runNode('fleet-bump-fg-devkit.mjs'))
1104
- if (choice === 'm8') {
1105
- const scope = await selectPrompt({
1106
- message: 'Bump pnpm toolchain',
1107
- choices: [
1108
- { name: 'fg-devkit only (policy, scaffold, install)', value: 'fg-devkit' },
1109
- { name: 'fg-devkit + pick pnpm repos under projects root', value: 'fleet' },
1110
- { name: 'Dry run + pick pnpm repos', value: 'dry-run-fleet' },
1111
- { name: 'Cancel', value: 'cancel' },
1112
- ],
1113
- default: 'fg-devkit',
1114
- loop: false,
1115
- })
1116
- if (scope === 'cancel') continue
1117
- const bumpArgs = []
1118
- if (scope === 'fg-devkit') {
1119
- bumpArgs.push('--skip-fleet')
1120
- }
1121
- if (scope === 'fleet') {
1122
- bumpArgs.push('--pick')
1123
- }
1124
- if (scope === 'dry-run-fleet') {
1125
- bumpArgs.push('--dry-run', '--pick')
1126
- }
1127
- return runSelectedAction(() => runNode('bump-pnpm-toolchain.mjs', bumpArgs))
1128
- }
1129
- if (choice === 'm9') {
1130
- const scope = await selectPrompt({
1131
- message: 'Bump Node toolchain',
1132
- choices: [
1133
- { name: 'fg-devkit only (policy + local files)', value: 'fg-devkit' },
1134
- { name: 'fg-devkit + pick projects under projects root', value: 'fleet' },
1135
- { name: 'Dry run + pick projects', value: 'dry-run-fleet' },
1136
- { name: 'Cancel', value: 'cancel' },
1137
- ],
1138
- default: 'fleet',
1139
- loop: false,
1140
- })
1141
- if (scope === 'cancel') continue
1142
- const bumpArgs = []
1143
- if (scope === 'fg-devkit') {
1144
- bumpArgs.push('--skip-fleet')
1145
- }
1146
- if (scope === 'fleet') {
1147
- bumpArgs.push('--pick')
1148
- }
1149
- if (scope === 'dry-run-fleet') {
1150
- bumpArgs.push('--dry-run', '--pick')
1151
- }
1152
- return runSelectedAction(() => runNode('bump-node-toolchain.mjs', bumpArgs))
1153
- }
1154
- if (choice === 'm10') {
1155
- const scope = await selectPrompt({
1156
- message: 'Git pull all repositories',
1157
- choices: [
1158
- { name: 'Pull all git repos under projects root', value: 'pull' },
1159
- { name: 'Dry run (all repos)', value: 'dry-run' },
1160
- { name: 'Cancel', value: 'cancel' },
1161
- ],
1162
- default: 'pull',
1163
- loop: false,
1164
- })
1165
- if (scope === 'cancel') continue
1166
- const pullArgs = scope === 'dry-run' ? ['--dry-run'] : []
1167
- return runSelectedAction(() => runNode('fleet-git-pull.mjs', pullArgs))
1168
- }
1169
- if (choice === 'm11') {
1170
- const scope = await selectPrompt({
1171
- message: 'Git pull (choose repositories)',
1172
- choices: [
1173
- { name: 'fg-devkit only', value: 'fg-devkit' },
1174
- { name: 'Choose repositories under projects root', value: 'pick' },
1175
- { name: 'Dry run + choose repositories', value: 'dry-run-pick' },
1176
- { name: 'Cancel', value: 'cancel' },
1177
- ],
1178
- default: 'pick',
1179
- loop: false,
1180
- })
1181
- if (scope === 'cancel') continue
1182
- const pullArgs = []
1183
- if (scope === 'fg-devkit') {
1184
- pullArgs.push('--fg-devkit-only')
1185
- }
1186
- if (scope === 'pick') {
1187
- pullArgs.push('--pick')
1188
- }
1189
- if (scope === 'dry-run-pick') {
1190
- pullArgs.push('--dry-run', '--pick')
1191
- }
1192
- return runSelectedAction(() => runNode('fleet-git-pull-scoped.mjs', pullArgs))
1150
+ if (choice === 'policy-outdated') return runSelectedAction(() => runNode('policy-outdated.mjs'))
1151
+ if (choice === 'policy-upgrade') return runSelectedAction(() => runNode('policy-upgrade.mjs'))
1152
+ if (choice === 'refresh-policy') {
1153
+ return runSelectedAction(() => runNode('refresh-dependency-policy-from-repos.mjs'))
1193
1154
  }
1194
- if (choice === 'm12') {
1155
+ if (choice === 'policy-vulns') return runSelectedAction(() => runNode('policy-vulns.mjs'))
1156
+ if (choice === 'bump-fg-devkit') return runSelectedAction(() => runNode('fleet-bump-fg-devkit.mjs'))
1157
+ if (choice === 'vulns') {
1195
1158
  const scope = await selectPrompt({
1196
- message: 'Stage, commit and push all repositories',
1159
+ message: 'Vulnerabilities',
1197
1160
  choices: [
1198
- { name: 'Stage, commit and push all repos', value: 'run' },
1199
- { name: 'Dry run (all repos)', value: 'dry-run' },
1161
+ {
1162
+ name: 'All Vue repos',
1163
+ value: 'all',
1164
+ description: 'pnpm audit in every repo under the projects root, with a summary',
1165
+ },
1166
+ {
1167
+ name: 'fg-devkit only',
1168
+ value: 'fg-devkit',
1169
+ description: 'pnpm audit in fg-devkit and its nested packages',
1170
+ },
1200
1171
  { name: 'Cancel', value: 'cancel' },
1201
1172
  ],
1202
- default: 'run',
1173
+ default: 'all',
1203
1174
  loop: false,
1204
1175
  })
1205
1176
  if (scope === 'cancel') continue
1206
- const commitArgs = scope === 'dry-run' ? ['--dry-run'] : []
1207
- return runSelectedAction(() => runNode('fleet-git-commit-push.mjs', commitArgs))
1177
+ return runSelectedAction(() =>
1178
+ scope === 'fg-devkit' ? runMaintainerAuditAll() : runNode('fleet-vulns.mjs'),
1179
+ )
1208
1180
  }
1209
- if (choice === 'm13') {
1210
- const scope = await selectPrompt({
1211
- message: 'Stage, commit and push (choose repositories)',
1212
- choices: [
1213
- { name: 'fg-devkit only', value: 'fg-devkit' },
1214
- { name: 'Choose repositories under projects root', value: 'pick' },
1215
- { name: 'Dry run + choose repositories', value: 'dry-run-pick' },
1216
- { name: 'Cancel', value: 'cancel' },
1217
- ],
1218
- default: 'pick',
1219
- loop: false,
1220
- })
1221
- if (scope === 'cancel') continue
1222
- const commitArgs = []
1223
- if (scope === 'fg-devkit') {
1224
- commitArgs.push('--fg-devkit-only')
1225
- }
1226
- if (scope === 'pick') {
1227
- commitArgs.push('--pick')
1228
- }
1229
- if (scope === 'dry-run-pick') {
1230
- commitArgs.push('--dry-run', '--pick')
1231
- }
1232
- return runSelectedAction(() => runNode('fleet-git-commit-push-scoped.mjs', commitArgs))
1233
- }
1234
- if (choice === 'm14') {
1235
- const scope = await selectPrompt({
1236
- message: 'Discard tracked changes in all repositories',
1237
- choices: [
1238
- { name: 'Discard tracked changes in all repos', value: 'run' },
1239
- { name: 'Dry run (all repos)', value: 'dry-run' },
1240
- { name: 'Cancel', value: 'cancel' },
1241
- ],
1242
- default: 'run',
1243
- loop: false,
1244
- })
1245
- if (scope === 'cancel') continue
1246
- const discardArgs = scope === 'dry-run' ? ['--dry-run'] : []
1247
- return runSelectedAction(() => runNode('fleet-git-discard.mjs', discardArgs))
1181
+
1182
+ // Toolchain bumps always update fg-devkit itself; the git actions treat it as one repo among the rest.
1183
+ const fleetActions = {
1184
+ 'bump-pnpm': { message: 'Bump pnpm', script: 'bump-pnpm-toolchain.mjs', toolchain: true, default: 'fg-devkit' },
1185
+ 'bump-node': { message: 'Bump Node', script: 'bump-node-toolchain.mjs', toolchain: true, default: 'pick' },
1186
+ 'git-pull': { message: 'Git pull', script: 'fleet-git-pull-scoped.mjs', default: 'pick' },
1187
+ 'git-commit-push': { message: 'Commit & push', script: 'fleet-git-commit-push-scoped.mjs', default: 'pick' },
1188
+ 'git-discard': { message: 'Discard changes', script: 'fleet-git-discard-scoped.mjs', default: 'pick' },
1248
1189
  }
1249
- if (choice === 'm15') {
1250
- const scope = await selectPrompt({
1251
- message: 'Discard tracked changes (choose repositories)',
1252
- choices: [
1253
- { name: 'fg-devkit only', value: 'fg-devkit' },
1254
- { name: 'Choose repositories under projects root', value: 'pick' },
1255
- { name: 'Dry run + choose repositories', value: 'dry-run-pick' },
1256
- { name: 'Cancel', value: 'cancel' },
1257
- ],
1258
- default: 'pick',
1259
- loop: false,
1260
- })
1261
- if (scope === 'cancel') continue
1262
- const discardArgs = []
1263
- if (scope === 'fg-devkit') {
1264
- discardArgs.push('--fg-devkit-only')
1265
- }
1266
- if (scope === 'pick') {
1267
- discardArgs.push('--pick')
1268
- }
1269
- if (scope === 'dry-run-pick') {
1270
- discardArgs.push('--dry-run', '--pick')
1271
- }
1272
- return runSelectedAction(() => runNode('fleet-git-discard-scoped.mjs', discardArgs))
1190
+ const action = fleetActions[choice]
1191
+ const scope = await selectPrompt({
1192
+ message: action.message,
1193
+ choices: [
1194
+ {
1195
+ name: action.toolchain ? 'fg-devkit + all repos' : 'All repos',
1196
+ value: 'all',
1197
+ description: 'Every repo under the projects root',
1198
+ },
1199
+ {
1200
+ name: interactive(action.toolchain ? 'fg-devkit + choose repos' : 'Choose repos'),
1201
+ value: 'pick',
1202
+ description: 'Pick repos in a checklist',
1203
+ },
1204
+ { name: 'fg-devkit only', value: 'fg-devkit', description: 'Only this repo' },
1205
+ { name: 'Dry run, all repos', value: 'dry-run-all', description: 'Show what would happen, change nothing' },
1206
+ {
1207
+ name: interactive('Dry run, choose repos'),
1208
+ value: 'dry-run-pick',
1209
+ description: 'Show what would happen, change nothing',
1210
+ },
1211
+ { name: 'Cancel', value: 'cancel' },
1212
+ ],
1213
+ default: action.default,
1214
+ loop: false,
1215
+ })
1216
+ if (scope === 'cancel') continue
1217
+ const scopeArgs = {
1218
+ all: ['--all'],
1219
+ pick: ['--pick'],
1220
+ 'fg-devkit': [action.toolchain ? '--skip-fleet' : '--fg-devkit-only'],
1221
+ 'dry-run-all': ['--dry-run', '--all'],
1222
+ 'dry-run-pick': ['--dry-run', '--pick'],
1273
1223
  }
1274
- return 1
1224
+ return runSelectedAction(() => runNode(action.script, scopeArgs[scope]))
1275
1225
  }
1276
1226
  }
1277
1227
 
@@ -1300,11 +1250,7 @@ async function main() {
1300
1250
  return
1301
1251
  }
1302
1252
  if (sub === 'commit') {
1303
- process.exitCode = await runCzg(rest)
1304
- return
1305
- }
1306
- if (sub === 'commit-vuln' || sub === 'vuln-commit' || sub === 'commit:vuln') {
1307
- process.exitCode = runNode('vuln-commit.mjs', rest)
1253
+ process.exitCode = await runCommit(rest)
1308
1254
  return
1309
1255
  }
1310
1256
  if (sub === 'scaffold-vue') {
@@ -1361,11 +1307,8 @@ async function main() {
1361
1307
  return
1362
1308
  }
1363
1309
  if (
1364
- (sub === 'fleet-git-pull' ||
1365
- sub === 'fleet-git-pull-scoped' ||
1366
- sub === 'fleet-git-commit-push' ||
1310
+ (sub === 'fleet-git-pull-scoped' ||
1367
1311
  sub === 'fleet-git-commit-push-scoped' ||
1368
- sub === 'fleet-git-discard' ||
1369
1312
  sub === 'fleet-git-discard-scoped') &&
1370
1313
  !isFgDevkitMaintainerContext()
1371
1314
  ) {