@smoothbricks/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +289 -0
  2. package/bin/smoo +4 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +141 -0
  6. package/dist/github-ci/index.d.ts +11 -0
  7. package/dist/github-ci/index.d.ts.map +1 -0
  8. package/dist/github-ci/index.js +174 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +1 -0
  12. package/dist/lib/json.d.ts +13 -0
  13. package/dist/lib/json.d.ts.map +1 -0
  14. package/dist/lib/json.js +57 -0
  15. package/dist/lib/run.d.ts +5 -0
  16. package/dist/lib/run.d.ts.map +1 -0
  17. package/dist/lib/run.js +64 -0
  18. package/dist/lib/workspace.d.ts +23 -0
  19. package/dist/lib/workspace.d.ts.map +1 -0
  20. package/dist/lib/workspace.js +101 -0
  21. package/dist/monorepo/commit-msg.d.ts +2 -0
  22. package/dist/monorepo/commit-msg.d.ts.map +1 -0
  23. package/dist/monorepo/commit-msg.js +34 -0
  24. package/dist/monorepo/git-config.d.ts +2 -0
  25. package/dist/monorepo/git-config.d.ts.map +1 -0
  26. package/dist/monorepo/git-config.js +37 -0
  27. package/dist/monorepo/index.d.ts +19 -0
  28. package/dist/monorepo/index.d.ts.map +1 -0
  29. package/dist/monorepo/index.js +59 -0
  30. package/dist/monorepo/lockfile.d.ts +2 -0
  31. package/dist/monorepo/lockfile.d.ts.map +1 -0
  32. package/dist/monorepo/lockfile.js +34 -0
  33. package/dist/monorepo/managed-files.d.ts +8 -0
  34. package/dist/monorepo/managed-files.d.ts.map +1 -0
  35. package/dist/monorepo/managed-files.js +130 -0
  36. package/dist/monorepo/nx-sync.d.ts +2 -0
  37. package/dist/monorepo/nx-sync.d.ts.map +1 -0
  38. package/dist/monorepo/nx-sync.js +5 -0
  39. package/dist/monorepo/package-hygiene.d.ts +3 -0
  40. package/dist/monorepo/package-hygiene.d.ts.map +1 -0
  41. package/dist/monorepo/package-hygiene.js +12 -0
  42. package/dist/monorepo/package-policy.d.ts +8 -0
  43. package/dist/monorepo/package-policy.d.ts.map +1 -0
  44. package/dist/monorepo/package-policy.js +279 -0
  45. package/dist/monorepo/packed-package.d.ts +2 -0
  46. package/dist/monorepo/packed-package.d.ts.map +1 -0
  47. package/dist/monorepo/packed-package.js +92 -0
  48. package/dist/monorepo/packs/index.d.ts +10 -0
  49. package/dist/monorepo/packs/index.d.ts.map +1 -0
  50. package/dist/monorepo/packs/index.js +87 -0
  51. package/dist/monorepo/runtime.d.ts +2 -0
  52. package/dist/monorepo/runtime.d.ts.map +1 -0
  53. package/dist/monorepo/runtime.js +37 -0
  54. package/dist/release/index.d.ts +24 -0
  55. package/dist/release/index.d.ts.map +1 -0
  56. package/dist/release/index.js +325 -0
  57. package/managed/raw/.git-format-staged.yml +47 -0
  58. package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +66 -0
  59. package/managed/raw/tooling/git-hooks/commit-msg.sh +9 -0
  60. package/managed/raw/tooling/git-hooks/pre-commit.sh +19 -0
  61. package/managed/templates/github/actions/cache-nix-devenv/action.yml +79 -0
  62. package/managed/templates/github/actions/cache-node-modules/action.yml +13 -0
  63. package/managed/templates/github/actions/cache-nx/action.yml +21 -0
  64. package/managed/templates/github/actions/save-nix-devenv/action.yml +39 -0
  65. package/managed/templates/github/actions/setup-devenv/action.yml +63 -0
  66. package/managed/templates/github/workflows/ci.yml +98 -0
  67. package/managed/templates/github/workflows/publish.yml +136 -0
  68. package/package.json +68 -0
  69. package/src/cli.ts +149 -0
  70. package/src/github-ci/index.ts +196 -0
  71. package/src/index.ts +1 -0
  72. package/src/lib/json.ts +68 -0
  73. package/src/lib/run.ts +76 -0
  74. package/src/lib/workspace.ts +125 -0
  75. package/src/monorepo/commit-msg.ts +35 -0
  76. package/src/monorepo/git-config.ts +43 -0
  77. package/src/monorepo/index.ts +78 -0
  78. package/src/monorepo/lockfile.ts +37 -0
  79. package/src/monorepo/managed-files.ts +153 -0
  80. package/src/monorepo/nx-sync.ts +6 -0
  81. package/src/monorepo/package-hygiene.ts +14 -0
  82. package/src/monorepo/package-policy.ts +307 -0
  83. package/src/monorepo/packed-package.ts +101 -0
  84. package/src/monorepo/packs/index.ts +115 -0
  85. package/src/monorepo/runtime.ts +41 -0
  86. package/src/release/index.ts +394 -0
@@ -0,0 +1,63 @@
1
+ name: Setup Nix/devenv
2
+ description: Restore caches and build the SmoothBricks devenv shell after checkout.
3
+
4
+ outputs:
5
+ nix-cache-hit:
6
+ description: Whether the Nix profile/store cache restored from the exact primary key.
7
+ value: ${{ steps.nix-cache.outputs.cache-hit }}
8
+ devenv-cache-hit:
9
+ description: Whether the devenv/direnv cache restored from the exact primary key.
10
+ value: ${{ steps.devenv-cache.outputs.cache-hit }}
11
+
12
+ runs:
13
+ using: composite
14
+ steps:
15
+ # This action is intentionally post-checkout only. Repo-local actions do not
16
+ # exist before actions/checkout has populated the working tree.
17
+ - name: ❄️ Install Nix
18
+ uses: DeterminateSystems/nix-installer-action@v22
19
+ with:
20
+ extra-conf: |
21
+ keep-outputs = true
22
+ keep-derivations = true
23
+ gc-keep-derivations = true
24
+ gc-keep-outputs = true
25
+
26
+ - name: 🧊 Cache Nix profiles and store NAR
27
+ id: nix-cache
28
+ uses: ./.github/actions/cache-nix-devenv
29
+ with:
30
+ segment: nix
31
+
32
+ - name: ♻️ Restore Nix store from NAR
33
+ shell: bash
34
+ working-directory: tooling/direnv
35
+ run: ./github-actions-bootstrap.sh restore-store
36
+
37
+ # Restore direnv state only when the Nix cache key is exact. Partial Nix
38
+ # restores can contain a NAR for a different shell closure, and .direnv
39
+ # points directly at derivations inside that closure.
40
+ - name: 🧊 Cache devenv and direnv
41
+ id: devenv-cache
42
+ if: ${{ steps.nix-cache.outputs.cache-hit == 'true' }}
43
+ uses: ./.github/actions/cache-nix-devenv
44
+ with:
45
+ segment: devenv
46
+
47
+ - name: ⚡ Enable Cachix
48
+ uses: cachix/cachix-action@v17
49
+ with:
50
+ name: devenv
51
+
52
+ - name: 🧰 Install devenv
53
+ shell: bash
54
+ working-directory: tooling/direnv
55
+ run: ./github-actions-bootstrap.sh install-devenv
56
+
57
+ - name: 📦 Cache node_modules
58
+ uses: ./.github/actions/cache-node-modules
59
+
60
+ - name: 🐚 Build devenv shell
61
+ shell: bash
62
+ working-directory: tooling/direnv
63
+ run: ./github-actions-bootstrap.sh build-shell
@@ -0,0 +1,98 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ actions: read
9
+ contents: read
10
+ statuses: write
11
+
12
+ defaults:
13
+ run:
14
+ working-directory: tooling/direnv
15
+
16
+ jobs:
17
+ main:
18
+ runs-on: ubuntu-latest
19
+ env:
20
+ NIX_STORE_NAR: ${{ github.workspace }}/nix-store.nar
21
+ GH_TOKEN: ${{ github.token }}
22
+ steps:
23
+ # Step 1: GitHub adds "Set up job" automatically
24
+ # Step 2
25
+ - name: 📥 Checkout
26
+ uses: actions/checkout@v6.0.2
27
+ with:
28
+ filter: blob:none
29
+ fetch-depth: 0
30
+
31
+ # Step 3. Composite action internals do not affect top-level job step
32
+ # anchors; update the nx-smart --step values below if top-level steps move.
33
+ - name: 🧱 Setup Nix/devenv
34
+ id: setup
35
+ uses: ./.github/actions/setup-devenv
36
+
37
+ # --- Nx -----------------------------------------------------------------
38
+
39
+ # Step 4
40
+ # Sets the base and head SHAs required for the nx affected commands
41
+ - name: 🧭 Set Nx SHAs
42
+ uses: nrwl/nx-set-shas@v5.0.1
43
+ with:
44
+ workflow-id: ci.yml
45
+
46
+ # Step 5
47
+ - name: 🧠 Restore Nx cache
48
+ id: nx-cache
49
+ uses: ./.github/actions/cache-nx
50
+
51
+ # Step 6
52
+ - name: 🔨 Build
53
+ run: smoo github-ci nx-smart --target build --name "Build" --step 6
54
+
55
+ # Step 7
56
+ - name: 🔍 Lint
57
+ run: smoo github-ci nx-smart --target lint --name "Lint" --step 7
58
+
59
+ # Step 8
60
+ - name: 🧪 Unit Tests
61
+ run: smoo github-ci nx-smart --target test --name "Unit Tests" --step 8
62
+
63
+ # Step 9
64
+ # Nx's database cache needs artifact files and .nx/workspace-data DB
65
+ # metadata restored together; GitHub Actions cache is only the archive
66
+ # transport. Save runs only after build/lint/test succeed on the default
67
+ # branch, so PRs may restore shared cache but cannot publish it.
68
+ - name: 💾 Save Nx cache
69
+ if:
70
+ ${{ github.event_name == 'push' && github.ref == format('refs/heads/{0}',
71
+ github.event.repository.default_branch) && steps.nx-cache.outputs.cache-hit != 'true' }}
72
+ uses: actions/cache/save@v5.0.5
73
+ with:
74
+ path: |
75
+ .nx/cache
76
+ .nx/workspace-data/*.db*
77
+ key: ${{ runner.os }}-nx-db-v1-${{ github.sha }}
78
+
79
+ # Step 10
80
+ - name: 📎 Upload trace DBs
81
+ if: ${{ failure() }}
82
+ uses: actions/upload-artifact@v4
83
+ with:
84
+ name: trace-results-${{ github.run_id }}
85
+ path: packages/*/.trace-results.db
86
+ if-no-files-found: ignore
87
+ retention-days: 14
88
+ include-hidden-files: true
89
+
90
+ # --- Cleanup ------------------------------------------------------------
91
+
92
+ # Step 11
93
+ - name: 🧹 Cleanup and cache Nix/devenv
94
+ if: ${{ always() }}
95
+ uses: ./.github/actions/save-nix-devenv
96
+ with:
97
+ nix-cache-hit: ${{ steps.setup.outputs.nix-cache-hit }}
98
+ devenv-cache-hit: ${{ steps.setup.outputs.devenv-cache-hit }}
@@ -0,0 +1,136 @@
1
+ name: Publish
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ bump:
7
+ type: choice
8
+ description:
9
+ Use auto for conventional commits, or force a semver bump. Prerelease publishes to next; all others publish to
10
+ latest.
11
+ options: [auto, patch, minor, major, prerelease]
12
+ default: auto
13
+ dry_run:
14
+ type: boolean
15
+ description: Run release commands without writing versions, tags, publishes, or GitHub Releases.
16
+ default: false
17
+
18
+ permissions:
19
+ contents: write
20
+ id-token: write
21
+
22
+ concurrency:
23
+ group: release-${{ github.ref }}
24
+ cancel-in-progress: false
25
+
26
+ defaults:
27
+ run:
28
+ working-directory: tooling/direnv
29
+
30
+ jobs:
31
+ publish:
32
+ runs-on: ubuntu-latest
33
+ env:
34
+ NIX_STORE_NAR: ${{ github.workspace }}/nix-store.nar
35
+ GH_TOKEN: ${{ github.token }}
36
+ steps:
37
+ # --- Setup --------------------------------------------------------------
38
+
39
+ # Step 1: GitHub adds "Set up job" automatically
40
+ # Step 2
41
+ - name: 📥 Checkout
42
+ uses: actions/checkout@v6.0.2
43
+ with:
44
+ filter: blob:none
45
+ fetch-depth: 0
46
+
47
+ # Step 3. Composite action internals do not affect top-level job step
48
+ # anchors; update these comments if top-level steps move.
49
+ - name: 🧱 Setup Nix/devenv
50
+ id: setup
51
+ uses: ./.github/actions/setup-devenv
52
+
53
+ # --- Validation ---------------------------------------------------------
54
+
55
+ # Release validation intentionally does not restore persisted Nx task
56
+ # cache. Nx may reuse tasks produced earlier in this same job, but publish
57
+ # never relies on task outputs restored from CI cache.
58
+
59
+ # Step 4
60
+ - name: ✅ Check managed monorepo files
61
+ run: smoo monorepo check
62
+
63
+ # Step 5
64
+ - name: 📦 Resolve release projects
65
+ id: projects
66
+ run: echo "projects=$(smoo monorepo list-public-projects)" >> "$GITHUB_OUTPUT"
67
+
68
+ # Step 6
69
+ - name: 🔨 Build
70
+ run: smoo github-ci nx-run-many --targets build --projects "${{ steps.projects.outputs.projects }}"
71
+
72
+ # Step 7
73
+ - name: 🔍 Lint
74
+ run: smoo github-ci nx-run-many --targets lint --projects "${{ steps.projects.outputs.projects }}"
75
+
76
+ # Step 8
77
+ - name: 🧪 Unit Tests
78
+ run: smoo github-ci nx-run-many --targets test --projects "${{ steps.projects.outputs.projects }}"
79
+
80
+ # Step 9
81
+ - name: 📎 Upload trace DBs
82
+ if: ${{ failure() }}
83
+ uses: actions/upload-artifact@v4
84
+ with:
85
+ name: trace-results-${{ github.run_id }}
86
+ path: packages/*/.trace-results.db
87
+ if-no-files-found: ignore
88
+ retention-days: 14
89
+ include-hidden-files: true
90
+
91
+ # Step 10
92
+ - name: ✅ Validate monorepo config
93
+ run: smoo monorepo validate
94
+
95
+ # --- Release ------------------------------------------------------------
96
+
97
+ # Step 11
98
+ - name: 🤖 Configure release author
99
+ run:
100
+ git config user.name "github-actions[bot]" && git config user.email
101
+ "41898282+github-actions[bot]@users.noreply.github.com"
102
+
103
+ # Step 12
104
+ - name: 🏷️ Version, tag, and push packages
105
+ run: smoo release version --bump "${{ inputs.bump }}" --dry-run "${{ inputs.dry_run }}"
106
+
107
+ # Step 13
108
+ - name: 🔖 Collect release tags
109
+ id: tags
110
+ run: echo "tags=$(git tag --points-at HEAD | tr '\n' ' ')" >> "$GITHUB_OUTPUT"
111
+
112
+ # Step 14
113
+ - name: 📦 Publish packages
114
+ # smoo packs with Bun, then publishes tarballs with npm. npm uses OIDC
115
+ # trusted publishing when configured; NODE_AUTH_TOKEN is only a temporary
116
+ # bootstrap fallback before packages have trusted publishers.
117
+ env:
118
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
119
+ run: smoo release publish --bump "${{ inputs.bump }}" --dry-run "${{ inputs.dry_run }}"
120
+
121
+ # Step 15
122
+ - name: 🚀 Create or update GitHub Releases
123
+ if: ${{ inputs.dry_run != true }}
124
+ run:
125
+ smoo release github-release --tags "${{ steps.tags.outputs.tags }}" --bump "${{ inputs.bump }}" --dry-run "${{
126
+ inputs.dry_run }}"
127
+
128
+ # --- Cleanup ------------------------------------------------------------
129
+
130
+ # Step 16
131
+ - name: 🧹 Cleanup and cache Nix/devenv
132
+ if: ${{ always() }}
133
+ uses: ./.github/actions/save-nix-devenv
134
+ with:
135
+ nix-cache-hit: ${{ steps.setup.outputs.nix-cache-hit }}
136
+ devenv-cache-hit: ${{ steps.setup.outputs.devenv-cache-hit }}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@smoothbricks/cli",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "SmoothBricks monorepo automation CLI",
6
+ "bin": {
7
+ "smoo": "./bin/smoo"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ "./package.json": "./package.json",
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "development": "./src/index.ts",
17
+ "import": "./dist/index.js",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "./monorepo/runtime": {
21
+ "types": "./dist/monorepo/runtime.d.ts",
22
+ "bun": "./src/monorepo/runtime.ts",
23
+ "development": "./src/monorepo/runtime.ts",
24
+ "import": "./dist/monorepo/runtime.js",
25
+ "default": "./dist/monorepo/runtime.js"
26
+ },
27
+ "./monorepo/git-config": {
28
+ "types": "./dist/monorepo/git-config.d.ts",
29
+ "bun": "./src/monorepo/git-config.ts",
30
+ "development": "./src/monorepo/git-config.ts",
31
+ "import": "./dist/monorepo/git-config.js",
32
+ "default": "./dist/monorepo/git-config.js"
33
+ }
34
+ },
35
+ "files": [
36
+ "README.md",
37
+ "bin",
38
+ "dist",
39
+ "managed",
40
+ "src",
41
+ "!**/*.tsbuildinfo"
42
+ ],
43
+ "dependencies": {
44
+ "@arethetypeswrong/cli": "^0.18.2",
45
+ "@smoothbricks/validation": "0.0.1",
46
+ "commander": "^14.0.3",
47
+ "publint": "^0.3.18",
48
+ "sherif": "^1.11.1",
49
+ "tslib": "^2.8.1"
50
+ },
51
+ "nx": {
52
+ "targets": {
53
+ "lint": {}
54
+ },
55
+ "tags": [
56
+ "npm:public"
57
+ ]
58
+ },
59
+ "license": "MIT",
60
+ "publishConfig": {
61
+ "access": "public"
62
+ },
63
+ "repository": {
64
+ "type": "git",
65
+ "url": "git+https://github.com/smoothbricks/codebase.git",
66
+ "directory": "packages/cli"
67
+ }
68
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,149 @@
1
+ import { Command, CommanderError } from 'commander';
2
+ import { findRepoRoot } from './lib/run.js';
3
+
4
+ export async function runCli(argv = process.argv.slice(2)): Promise<void> {
5
+ const program = buildProgram();
6
+ try {
7
+ await program.parseAsync(argv, { from: 'user' });
8
+ } catch (error) {
9
+ if (error instanceof CommanderError) {
10
+ if (error.code !== 'commander.helpDisplayed') {
11
+ process.exitCode = error.exitCode;
12
+ }
13
+ return;
14
+ }
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ console.error(message);
17
+ process.exitCode = 1;
18
+ }
19
+ }
20
+
21
+ function buildProgram(): Command {
22
+ const program = new Command();
23
+ program.name('smoo').description('SmoothBricks monorepo tooling').exitOverride().showHelpAfterError();
24
+
25
+ const monorepo = program.command('monorepo').description('Manage SmoothBricks-style monorepos');
26
+ monorepo
27
+ .command('init')
28
+ .option('--runtime-only', 'only sync root Bun/Node runtime metadata')
29
+ .option('--sync-runtime', 'sync root Bun/Node runtime metadata outside devenv')
30
+ .action(async (options: { runtimeOnly?: boolean; syncRuntime?: boolean }) => {
31
+ const { initMonorepo } = await import('./monorepo/index.js');
32
+ await initMonorepo(await findRepoRoot(), options);
33
+ });
34
+ monorepo
35
+ .command('validate')
36
+ .option('--fail-fast', 'stop after the first failing validation pack')
37
+ .action(async (options: { failFast?: boolean }) => {
38
+ const { validateMonorepo } = await import('./monorepo/index.js');
39
+ await validateMonorepo(await findRepoRoot(), options);
40
+ });
41
+ monorepo.command('update').action(async () => {
42
+ const { updateManagedFiles } = await import('./monorepo/index.js');
43
+ updateManagedFiles(await findRepoRoot());
44
+ });
45
+ monorepo.command('check').action(async () => {
46
+ const { checkManagedFiles } = await import('./monorepo/index.js');
47
+ checkManagedFiles(await findRepoRoot());
48
+ });
49
+ monorepo.command('diff').action(async () => {
50
+ const { diffManagedFiles } = await import('./monorepo/index.js');
51
+ diffManagedFiles(await findRepoRoot());
52
+ });
53
+ monorepo.command('validate-commit-msg <commitMsgFile>').action(async (commitMsgFile: string) => {
54
+ const { validateCommitMessageFile } = await import('./monorepo/index.js');
55
+ validateCommitMessageFile(commitMsgFile);
56
+ });
57
+ monorepo.command('sync-bun-lockfile-versions').action(async () => {
58
+ const { syncBunLockfileVersions } = await import('./monorepo/index.js');
59
+ syncBunLockfileVersions(await findRepoRoot());
60
+ });
61
+ monorepo.command('list-public-projects').action(async () => {
62
+ const { listPublicProjects } = await import('./monorepo/index.js');
63
+ console.log(listPublicProjects(await findRepoRoot()));
64
+ });
65
+ monorepo.command('validate-public-tags').action(async () => {
66
+ const { validatePublicPackageTags } = await import('./monorepo/index.js');
67
+ validatePublicPackageTags(await findRepoRoot());
68
+ });
69
+ monorepo.command('release-state').action(async () => {
70
+ const { printReleaseState } = await import('./release/index.js');
71
+ await printReleaseState(await findRepoRoot());
72
+ });
73
+
74
+ const release = program.command('release').description('Version, publish, and create GitHub Releases');
75
+ release
76
+ .command('version')
77
+ .option('--bump <bump>', 'auto, patch, minor, major, or prerelease', 'auto')
78
+ .option('--dry-run [dryRun]', 'run without writing versions, tags, or pushes')
79
+ .action(async (options: { bump: string; dryRun?: string | boolean }) => {
80
+ const { releaseVersion } = await import('./release/index.js');
81
+ await releaseVersion(await findRepoRoot(), { bump: options.bump, dryRun: booleanOption(options.dryRun) });
82
+ });
83
+ release
84
+ .command('publish')
85
+ .option('--bump <bump>', 'auto, patch, minor, major, or prerelease', 'auto')
86
+ .option('--tag <tag>', 'explicit npm dist-tag; must match bump-derived tag')
87
+ .option('--npm-tag <npmTag>', 'explicit npm dist-tag; must match bump-derived tag')
88
+ .option('--dry-run [dryRun]', 'run without publishing')
89
+ .action(async (options: { bump: string; tag?: string; npmTag?: string; dryRun?: string | boolean }) => {
90
+ const { releasePublish } = await import('./release/index.js');
91
+ await releasePublish(await findRepoRoot(), { ...options, dryRun: booleanOption(options.dryRun) });
92
+ });
93
+ release
94
+ .command('github-release')
95
+ .option('--tags <tags>', 'space-separated release tags')
96
+ .option('--bump <bump>', 'auto, patch, minor, major, or prerelease', 'auto')
97
+ .option('--tag <tag>', 'explicit npm dist-tag; must match bump-derived tag')
98
+ .option('--npm-tag <npmTag>', 'explicit npm dist-tag; must match bump-derived tag')
99
+ .option('--dry-run [dryRun]', 'skip GitHub Release writes')
100
+ .action(
101
+ async (options: { tags?: string; bump: string; tag?: string; npmTag?: string; dryRun?: string | boolean }) => {
102
+ const { releaseGithubRelease } = await import('./release/index.js');
103
+ await releaseGithubRelease(await findRepoRoot(), { ...options, dryRun: booleanOption(options.dryRun) });
104
+ },
105
+ );
106
+ release
107
+ .command('trust-publisher')
108
+ .description('Configure npm trusted publishing for public packages')
109
+ .option('--dry-run [dryRun]', 'show npm trust changes without saving them')
110
+ .option('--otp <otp>', 'npm one-time password for trust operations')
111
+ .option('--skip-login', 'skip npm browser login before configuring trust')
112
+ .action(async (options: { dryRun?: string | boolean; otp?: string; skipLogin?: boolean }) => {
113
+ const { releaseTrustPublisher } = await import('./release/index.js');
114
+ await releaseTrustPublisher(await findRepoRoot(), {
115
+ dryRun: booleanOption(options.dryRun),
116
+ otp: options.otp,
117
+ skipLogin: options.skipLogin === true,
118
+ });
119
+ });
120
+
121
+ const githubCi = program.command('github-ci').description('GitHub Actions helpers');
122
+ githubCi.command('cleanup-cache').action(async () => {
123
+ const { cleanupGithubCiCache } = await import('./github-ci/index.js');
124
+ await cleanupGithubCiCache(await findRepoRoot());
125
+ });
126
+ githubCi
127
+ .command('nx-smart')
128
+ .requiredOption('--target <target>')
129
+ .option('--name <name>')
130
+ .option('--step <step>')
131
+ .action(async (options: { target: string; name?: string; step?: string }) => {
132
+ const { githubCiNxSmart } = await import('./github-ci/index.js');
133
+ await githubCiNxSmart(await findRepoRoot(), options);
134
+ });
135
+ githubCi
136
+ .command('nx-run-many')
137
+ .requiredOption('--targets <targets>')
138
+ .option('--projects <projects>')
139
+ .action(async (options: { targets: string; projects?: string }) => {
140
+ const { githubCiNxRunMany } = await import('./github-ci/index.js');
141
+ await githubCiNxRunMany(await findRepoRoot(), options);
142
+ });
143
+
144
+ return program;
145
+ }
146
+
147
+ function booleanOption(value: string | boolean | undefined): boolean {
148
+ return value === true || value === 'true';
149
+ }