From 93ceff7b96133c0248630df4d81a073c0bdfbbde Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 00:31:20 +0100 Subject: [PATCH 01/16] ci: repair release action pin --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b744ab8..a00f618 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,7 +44,7 @@ jobs: - name: Select Changesets mode id: mode - uses: changesets/action/select-mode@0138f456ec3d73906fcd11169ce59502d8d241c1 + uses: changesets/action/select-mode@22ccf9aa43179fe9e27dc62e575971d28cce197c # v2.0.0 version: name: Create or update release PR @@ -72,7 +72,7 @@ jobs: run: npm ci --ignore-scripts --no-audit --no-fund - name: Create or update release PR - uses: changesets/action/version@0138f456ec3d73906fcd11169ce59502d8d241c1 + uses: changesets/action/version@22ccf9aa43179fe9e27dc62e575971d28cce197c # v2.0.0 with: github-token: ${{ github.token }} pr-title: 'release: version packages' @@ -117,7 +117,7 @@ jobs: - name: Pack exact Changesets publish artifacts id: pack - uses: changesets/action/pack@0138f456ec3d73906fcd11169ce59502d8d241c1 + uses: changesets/action/pack@22ccf9aa43179fe9e27dc62e575971d28cce197c # v2.0.0 with: publish-plan-artifact-id: ${{ needs.select-mode.outputs.publish-plan-artifact-id }} @@ -157,7 +157,7 @@ jobs: - name: Publish packed artifacts id: publish - uses: changesets/action/publish@0138f456ec3d73906fcd11169ce59502d8d241c1 + uses: changesets/action/publish@22ccf9aa43179fe9e27dc62e575971d28cce197c # v2.0.0 with: github-token: ${{ github.token }} pack-dir-artifact-id: ${{ needs.pack.outputs.pack-dir-artifact-id }} From 76ee7152bc650052ccb9c4bf6599159cb7383278 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 00:31:21 +0100 Subject: [PATCH 02/16] test: stabilize api-service-template.test.ts --- tests/api-service-template.test.ts | 96 ++++++++++++++++-------------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/tests/api-service-template.test.ts b/tests/api-service-template.test.ts index 9b6c780..d981106 100644 --- a/tests/api-service-template.test.ts +++ b/tests/api-service-template.test.ts @@ -20,57 +20,61 @@ function runNpmScript(projectRoot: string, script: string) { } describe('API Service template', () => { - it('generates a runnable dependency-light service with isolated Stellar configuration', () => - withTempDirectory((root) => { - const captured = createCapturedTerminalOutput(); - const command = createNewCommand({ - cwd: () => root, - output: captured.output, - }); + it( + 'generates a runnable dependency-light service with isolated Stellar configuration', + () => + withTempDirectory((root) => { + const captured = createCapturedTerminalOutput(); + const command = createNewCommand({ + cwd: () => root, + output: captured.output, + }); - command.parse(['node', 'new', 'demo-api', '--template', 'api-service']); + command.parse(['node', 'new', 'demo-api', '--template', 'api-service']); - const projectRoot = join(root, 'demo-api'); - expect(existsSync(join(projectRoot, 'src', 'server.js'))).toBe(true); - expect(existsSync(join(projectRoot, 'src', 'stellar-client.js'))).toBe( - true, - ); - expect(existsSync(join(projectRoot, 'test', 'server.test.js'))).toBe( - true, - ); - expect(captured.stdoutText()).toContain( - 'Created demo-api from api-service', - ); + const projectRoot = join(root, 'demo-api'); + expect(existsSync(join(projectRoot, 'src', 'server.js'))).toBe(true); + expect(existsSync(join(projectRoot, 'src', 'stellar-client.js'))).toBe( + true, + ); + expect(existsSync(join(projectRoot, 'test', 'server.test.js'))).toBe( + true, + ); + expect(captured.stdoutText()).toContain( + 'Created demo-api from api-service', + ); - const packageManifest = readFileSync( - join(projectRoot, 'package.json'), - 'utf8', - ); - expect(packageManifest).toContain('"name": "demo-api"'); - expect(packageManifest).not.toContain('dependencies'); + const packageManifest = readFileSync( + join(projectRoot, 'package.json'), + 'utf8', + ); + expect(packageManifest).toContain('"name": "demo-api"'); + expect(packageManifest).not.toContain('dependencies'); - const environmentExample = readFileSync( - join(projectRoot, '.env.example'), - 'utf8', - ); - expect(environmentExample).toContain('STELLAR_NETWORK=TESTNET'); - expect(environmentExample).toContain('STELLAR_HORIZON_URL=https://'); - expect(environmentExample).toContain('STELLAR_RPC_URL=https://'); - expect(environmentExample).not.toMatch(/SECRET|SEED|PRIVATE_KEY/); + const environmentExample = readFileSync( + join(projectRoot, '.env.example'), + 'utf8', + ); + expect(environmentExample).toContain('STELLAR_NETWORK=TESTNET'); + expect(environmentExample).toContain('STELLAR_HORIZON_URL=https://'); + expect(environmentExample).toContain('STELLAR_RPC_URL=https://'); + expect(environmentExample).not.toMatch(/SECRET|SEED|PRIVATE_KEY/); - const check = runNpmScript(projectRoot, 'check'); - expect(check.status, check.stderr).toBe(0); + const check = runNpmScript(projectRoot, 'check'); + expect(check.status, check.stderr).toBe(0); - const test = runNpmScript(projectRoot, 'test'); - expect(test.status, test.stderr).toBe(0); + const test = runNpmScript(projectRoot, 'test'); + expect(test.status, test.stderr).toBe(0); - const generatedReadme = readFileSync( - join(projectRoot, 'README.md'), - 'utf8', - ); - expect(generatedReadme).toContain('GET /health'); - expect(generatedReadme).toContain('src/stellar-client.js'); - expect(generatedReadme).toContain('Never commit secret keys'); - expect(generatedReadme).toContain('official Stellar documentation'); - })); + const generatedReadme = readFileSync( + join(projectRoot, 'README.md'), + 'utf8', + ); + expect(generatedReadme).toContain('GET /health'); + expect(generatedReadme).toContain('src/stellar-client.js'); + expect(generatedReadme).toContain('Never commit secret keys'); + expect(generatedReadme).toContain('official Stellar documentation'); + }), + 15_000, + ); }); From 7aa889b85dd38efb0a9f9717e247eddb5f95b0f9 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 00:31:23 +0100 Subject: [PATCH 03/16] test: stabilize basic-app-template.test.ts --- tests/basic-app-template.test.ts | 82 ++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/tests/basic-app-template.test.ts b/tests/basic-app-template.test.ts index fa6b99e..562dea9 100644 --- a/tests/basic-app-template.test.ts +++ b/tests/basic-app-template.test.ts @@ -20,41 +20,49 @@ function runNpmScript(projectRoot: string, script: string) { } describe('Basic App template', () => { - it('generates through the default new command and passes its documented smoke checks', () => - withTempDirectory((root) => { - const captured = createCapturedTerminalOutput(); - const command = createNewCommand({ - cwd: () => root, - output: captured.output, - }); - - command.parse(['node', 'new', 'demo-app']); - - const projectRoot = join(root, 'demo-app'); - const packageJson = JSON.parse( - readFileSync(join(projectRoot, 'package.json'), 'utf8'), - ) as { name: string; dependencies?: unknown; devDependencies?: unknown }; - - expect(packageJson.name).toBe('demo-app'); - expect(packageJson.dependencies).toBeUndefined(); - expect(packageJson.devDependencies).toBeUndefined(); - expect(captured.stdoutText()).toContain( - 'Created demo-app from basic-app', - ); - - const check = runNpmScript(projectRoot, 'check'); - expect(check.status, check.stderr).toBe(0); - - const test = runNpmScript(projectRoot, 'test'); - expect(test.status, test.stderr).toBe(0); - - const generatedReadme = readFileSync( - join(projectRoot, 'README.md'), - 'utf8', - ); - expect(generatedReadme).toContain('# demo-app'); - expect(generatedReadme).toContain( - 'does not require or generate wallet secret keys', - ); - })); + it( + 'generates through the default new command and passes its documented smoke checks', + () => + withTempDirectory((root) => { + const captured = createCapturedTerminalOutput(); + const command = createNewCommand({ + cwd: () => root, + output: captured.output, + }); + + command.parse(['node', 'new', 'demo-app']); + + const projectRoot = join(root, 'demo-app'); + const packageJson = JSON.parse( + readFileSync(join(projectRoot, 'package.json'), 'utf8'), + ) as { + name: string; + dependencies?: unknown; + devDependencies?: unknown; + }; + + expect(packageJson.name).toBe('demo-app'); + expect(packageJson.dependencies).toBeUndefined(); + expect(packageJson.devDependencies).toBeUndefined(); + expect(captured.stdoutText()).toContain( + 'Created demo-app from basic-app', + ); + + const check = runNpmScript(projectRoot, 'check'); + expect(check.status, check.stderr).toBe(0); + + const test = runNpmScript(projectRoot, 'test'); + expect(test.status, test.stderr).toBe(0); + + const generatedReadme = readFileSync( + join(projectRoot, 'README.md'), + 'utf8', + ); + expect(generatedReadme).toContain('# demo-app'); + expect(generatedReadme).toContain( + 'does not require or generate wallet secret keys', + ); + }), + 15_000, + ); }); From fc79c933ed03abac44009c282f2ee5404ce43f27 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 00:31:25 +0100 Subject: [PATCH 04/16] test: stabilize full-stack-template.test.ts --- tests/full-stack-template.test.ts | 90 +++++++++++++++++-------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/tests/full-stack-template.test.ts b/tests/full-stack-template.test.ts index c90e7b9..6db840a 100644 --- a/tests/full-stack-template.test.ts +++ b/tests/full-stack-template.test.ts @@ -20,50 +20,60 @@ function runNpmScript(projectRoot: string, script: string) { } describe('Full Stack template', () => { - it('generates the documented frontend, backend, and contracts boundaries and passes smoke checks', () => - withTempDirectory((root) => { - const captured = createCapturedTerminalOutput(); - const command = createNewCommand({ - cwd: () => root, - output: captured.output, - }); + it( + 'generates the documented frontend, backend, and contracts boundaries and passes smoke checks', + () => + withTempDirectory((root) => { + const captured = createCapturedTerminalOutput(); + const command = createNewCommand({ + cwd: () => root, + output: captured.output, + }); - command.parse(['node', 'new', 'demo-stack', '--template', 'full-stack']); + command.parse([ + 'node', + 'new', + 'demo-stack', + '--template', + 'full-stack', + ]); - const projectRoot = join(root, 'demo-stack'); - expect(existsSync(join(projectRoot, 'frontend', 'src', 'app.js'))).toBe( - true, - ); - expect(existsSync(join(projectRoot, 'backend', 'src', 'server.js'))).toBe( - true, - ); - expect(existsSync(join(projectRoot, 'contracts', 'README.md'))).toBe( - true, - ); - expect(captured.stdoutText()).toContain( - 'Created demo-stack from full-stack', - ); + const projectRoot = join(root, 'demo-stack'); + expect(existsSync(join(projectRoot, 'frontend', 'src', 'app.js'))).toBe( + true, + ); + expect( + existsSync(join(projectRoot, 'backend', 'src', 'server.js')), + ).toBe(true); + expect(existsSync(join(projectRoot, 'contracts', 'README.md'))).toBe( + true, + ); + expect(captured.stdoutText()).toContain( + 'Created demo-stack from full-stack', + ); - const environmentExample = readFileSync( - join(projectRoot, '.env.example'), - 'utf8', - ); - expect(environmentExample).toContain('STELLAR_NETWORK=TESTNET'); - expect(environmentExample).not.toMatch(/SECRET|SEED|PRIVATE_KEY/); + const environmentExample = readFileSync( + join(projectRoot, '.env.example'), + 'utf8', + ); + expect(environmentExample).toContain('STELLAR_NETWORK=TESTNET'); + expect(environmentExample).not.toMatch(/SECRET|SEED|PRIVATE_KEY/); - const check = runNpmScript(projectRoot, 'check'); - expect(check.status, check.stderr).toBe(0); + const check = runNpmScript(projectRoot, 'check'); + expect(check.status, check.stderr).toBe(0); - const test = runNpmScript(projectRoot, 'test'); - expect(test.status, test.stderr).toBe(0); + const test = runNpmScript(projectRoot, 'test'); + expect(test.status, test.stderr).toBe(0); - const generatedReadme = readFileSync( - join(projectRoot, 'README.md'), - 'utf8', - ); - expect(generatedReadme).toContain('frontend/'); - expect(generatedReadme).toContain('backend/'); - expect(generatedReadme).toContain('contracts/'); - expect(generatedReadme).toContain('no credentials'); - })); + const generatedReadme = readFileSync( + join(projectRoot, 'README.md'), + 'utf8', + ); + expect(generatedReadme).toContain('frontend/'); + expect(generatedReadme).toContain('backend/'); + expect(generatedReadme).toContain('contracts/'); + expect(generatedReadme).toContain('no credentials'); + }), + 15_000, + ); }); From 486ab7f4f5cd3ba3c4a5cbaff794d74e8f94c1db Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 00:31:26 +0100 Subject: [PATCH 05/16] test: stabilize release-workflow.test.ts --- tests/release-workflow.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/release-workflow.test.ts b/tests/release-workflow.test.ts index 31eac36..48626e3 100644 --- a/tests/release-workflow.test.ts +++ b/tests/release-workflow.test.ts @@ -30,16 +30,16 @@ describe('release automation policy', () => { it('pins release actions and separates OIDC from versioning', () => { expect(workflow).toContain( - 'changesets/action/select-mode@0138f456ec3d73906fcd11169ce59502d8d241c1', + 'changesets/action/select-mode@22ccf9aa43179fe9e27dc62e575971d28cce197c', ); expect(workflow).toContain( - 'changesets/action/version@0138f456ec3d73906fcd11169ce59502d8d241c1', + 'changesets/action/version@22ccf9aa43179fe9e27dc62e575971d28cce197c', ); expect(workflow).toContain( - 'changesets/action/pack@0138f456ec3d73906fcd11169ce59502d8d241c1', + 'changesets/action/pack@22ccf9aa43179fe9e27dc62e575971d28cce197c', ); expect(workflow).toContain( - 'changesets/action/publish@0138f456ec3d73906fcd11169ce59502d8d241c1', + 'changesets/action/publish@22ccf9aa43179fe9e27dc62e575971d28cce197c', ); expect(workflow.match(/id-token: write/g)).toHaveLength(1); expect(workflow).toContain('environment: npm-release'); From 4e800ab4d5c67bf7cfee9a57252f3f6d838f998f Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 00:32:40 +0100 Subject: [PATCH 06/16] ci: exempt trusted maintainers from contributor-only Wave guard --- .github/workflows/wave-pr-guard.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/wave-pr-guard.yml b/.github/workflows/wave-pr-guard.yml index a08f173..1021250 100644 --- a/.github/workflows/wave-pr-guard.yml +++ b/.github/workflows/wave-pr-guard.yml @@ -23,6 +23,11 @@ jobs: const repo = context.repo.repo; const pr = context.payload.pull_request; const body = pr.body || ''; + const trustedAssociations = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']); + if (trustedAssociations.has(pr.author_association)) { + core.info('Skipping contributor-only Wave checks for trusted maintainer PR.'); + return; + } const refs = [...body.matchAll(/(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi)].map((m) => Number(m[1])); const uniqueRefs = [...new Set(refs)]; if (uniqueRefs.length !== 1) { From 4b7c11da1ac131f5329dc3ef15897ac71b6fa21c Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 00:33:29 +0100 Subject: [PATCH 07/16] ci: recognize repository write permission in Wave guard --- .github/workflows/wave-pr-guard.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/wave-pr-guard.yml b/.github/workflows/wave-pr-guard.yml index 1021250..0179666 100644 --- a/.github/workflows/wave-pr-guard.yml +++ b/.github/workflows/wave-pr-guard.yml @@ -28,6 +28,14 @@ jobs: core.info('Skipping contributor-only Wave checks for trusted maintainer PR.'); return; } + const { data: collaborator } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, repo, username: pr.user.login, + }); + const trustedPermissions = new Set(['admin', 'maintain', 'write']); + if (trustedPermissions.has(collaborator.permission)) { + core.info('Skipping contributor-only Wave checks for repository collaborator PR.'); + return; + } const refs = [...body.matchAll(/(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi)].map((m) => Number(m[1])); const uniqueRefs = [...new Set(refs)]; if (uniqueRefs.length !== 1) { From 29c3c6e04428043692f2692378c0655e60d3e63c Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 13:52:41 +0000 Subject: [PATCH 08/16] ci: recognize repository maintainers in Wave guard From 8fd0e6fe1e9fb0b8edfb5df85e3186b95c9288f7 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 13:52:43 +0000 Subject: [PATCH 09/16] docs: align Changesets guidance with protected publishing --- docs/contributing/changesets.md | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/docs/contributing/changesets.md b/docs/contributing/changesets.md index e804795..ce5bbf9 100644 --- a/docs/contributing/changesets.md +++ b/docs/contributing/changesets.md @@ -1,6 +1,6 @@ # Changesets Contributor Workflow -StellarForge CLI uses Changesets to record release intent close to the pull request that introduces a user-visible package change. The project is still pre-release and the npm package remains private, so this workflow currently supports local version and changelog generation only. It does not publish packages or create release tags. +StellarForge CLI uses Changesets to record release intent close to the pull request that introduces a user-visible package change. The package metadata is public-ready, while actual publication remains gated by npm Trusted Publishing, the protected `npm-release` environment, and the `NPM_PUBLISH_ENABLED` repository variable. ## When a Changeset Is Required @@ -31,7 +31,7 @@ Review the generated file before committing it. A typical file for this reposito ```markdown --- -"@stellarforge/cli": patch +"@diginodes/stellarforge-cli": patch --- Describe the user-visible change clearly. @@ -71,28 +71,8 @@ This command consumes pending changeset files and updates package versions/chang For ordinary feature pull requests, contributors should normally commit the changeset file itself rather than committing generated release-version changes. -## Private Package Behavior +## Protected Publishing -`@stellarforge/cli` is currently marked `private: true`. The Changesets configuration therefore explicitly enables private-package versioning while keeping private-package tags disabled: +The package is marked `private: false` and the Changesets configuration declares public access. That metadata alone does not authorize publication. The release workflow publishes only when npm Trusted Publishing is configured, the protected `npm-release` environment approves the job, and `NPM_PUBLISH_ENABLED` is exactly `true`. -```json -"privatePackages": { - "version": true, - "tag": false -} -``` - -This allows us to prove version/changelog generation during development without enabling package publication or release tagging. - -## Publishing Is Out of Scope - -The following are intentionally not part of the current Changesets integration: - -- `changeset publish`; -- npm publication; -- npm credentials or tokens; -- Git tags; -- GitHub Releases; -- release-publishing GitHub Actions. - -Protected publication automation will be implemented separately only after package identity, security gates, trusted publishing, and release-readiness requirements are satisfied. +Publishing uses GitHub Actions OIDC rather than a long-lived `NPM_TOKEN`. Maintainers must not run routine releases manually after the one-time registry bootstrap. From 234a76b47e4f9fcceb7035692e65c70e63778344 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 13:52:45 +0000 Subject: [PATCH 10/16] docs: adopt DigiNodes npm package identity --- docs/contributing/release-process.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/contributing/release-process.md b/docs/contributing/release-process.md index d068d38..27fe016 100644 --- a/docs/contributing/release-process.md +++ b/docs/contributing/release-process.md @@ -6,7 +6,7 @@ StellarForge CLI uses Semantic Versioning, Changesets, npm Trusted Publishing, a Release automation is intentionally merged in a non-publishing state. The workflow can create release PRs and validate/pack release artifacts, but the npm publish job runs only when **all** of these controls are in place: -1. the `@stellarforge/cli` npm scope/package is owned by the StellarForge maintainers; +1. the `@diginodes/stellarforge-cli` npm scope/package is owned by the StellarForge maintainers through the `diginodes` npm organization; 2. npm Trusted Publishing is configured for: - GitHub organization: `DigiNodes` - repository: `stellarforge-cli` @@ -68,14 +68,15 @@ From an approved maintainer workstation: 1. clone `DigiNodes/stellarforge-cli` and check out the bootstrap commit above; 2. run `npm ci --ignore-scripts --no-audit --no-fund`, `npm run build`, and `npm run release:dry-run`; -3. authenticate interactively to the npm account/organization that owns the `@stellarforge` scope, using the required 2FA flow; -4. publish `@stellarforge/cli@0.0.0` once with a non-default bootstrap tag, for example `npm publish --access public --tag bootstrap`; -5. configure npm Trusted Publishing on the newly existing `@stellarforge/cli` package for organization `DigiNodes`, repository `stellarforge-cli`, workflow `release.yml`, environment `npm-release`, with direct `npm publish` allowed; -6. create/protect the GitHub `npm-release` environment with required maintainer review; -7. set repository Actions variable `NPM_PUBLISH_ENABLED=true`; -8. enable GitHub Dependency Graph so the existing Dependency Review workflow becomes enforceable; -9. merge a reviewed release-activation change to `main` (or another approved `main` push) to trigger the protected publish path for the already-versioned `0.1.0` package; -10. approve the `npm-release` environment deployment and verify npm, Git tag, GitHub Release, and provenance all identify `0.1.0`. +3. apply the reviewed package-identity patch that changes only the npm package name to `@diginodes/stellarforge-cli` while retaining bootstrap version `0.0.0`; +4. authenticate interactively as an owner of the `diginodes` npm organization, using the required 2FA flow; +5. publish `@diginodes/stellarforge-cli@0.0.0` once with a non-default bootstrap tag, for example `npm publish --access public --tag bootstrap`; +6. configure npm Trusted Publishing on the newly existing `@diginodes/stellarforge-cli` package for organization `DigiNodes`, repository `stellarforge-cli`, workflow `release.yml`, environment `npm-release`, with direct `npm publish` allowed; +7. create/protect the GitHub `npm-release` environment with required maintainer review; +8. set repository Actions variable `NPM_PUBLISH_ENABLED=true`; +9. verify GitHub Dependency Graph keeps the existing Dependency Review workflow enforceable; +10. merge a reviewed release-activation change to `main` (or another approved `main` push) to trigger the protected publish path for the already-versioned `0.1.0` package; +11. approve the `npm-release` environment deployment and verify npm, Git tag, GitHub Release, and provenance all identify `0.1.0`. Do **not** republish, rewrite, or downgrade the `0.1.0` commit on `main` merely to bootstrap the npm namespace. @@ -89,7 +90,7 @@ The Changesets publish action is responsible for package publication, the packag After publication, verify: -- npm shows `@stellarforge/cli@`; +- npm shows `@diginodes/stellarforge-cli@`; - Git contains the matching package tag; - the GitHub Release points to the same version/tag; - npm displays provenance for the public package. From 4c4a87db5201d1df9c438978f30813e537056c78 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 13:52:46 +0000 Subject: [PATCH 11/16] docs: update pending npm package identity --- docs/guides/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/installation.md b/docs/guides/installation.md index a0fcbdf..8333879 100644 --- a/docs/guides/installation.md +++ b/docs/guides/installation.md @@ -2,7 +2,7 @@ StellarForge CLI has completed its initial MVP implementation and the repository contains protected release automation. The source is versioned at `0.1.0`, but the first public npm publication is still pending the one-time registry and Trusted Publishing setup tracked in REL-001. -Until that publication is verified, use a source checkout for development and evaluation rather than assuming `@stellarforge/cli` is available from the npm registry. +Until that publication is verified, use a source checkout for development and evaluation rather than assuming `@diginodes/stellarforge-cli` is available from the npm registry. ## Contributor prerequisites From 08b9f702947fb55f4d3cb88af60b78076797bfac Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 13:52:49 +0000 Subject: [PATCH 12/16] release: align lockfile with DigiNodes npm scope --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ce00fc2..14dab16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@stellarforge/cli", + "name": "@diginodes/stellarforge-cli", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@stellarforge/cli", + "name": "@diginodes/stellarforge-cli", "version": "0.1.0", "license": "MIT", "dependencies": { From 3b4777934b75670a43215138f02d12be0455e8f8 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 13:52:51 +0000 Subject: [PATCH 13/16] release: rename npm package for DigiNodes scope --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91c8dbd..b93905d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@stellarforge/cli", + "name": "@diginodes/stellarforge-cli", "version": "0.1.0", "private": false, "description": "Command-line interface for building production-ready Stellar applications.", From 0e11188ab9b4dcbcab1505c368ba56008a0d151a Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Mon, 21 Sep 2026 15:37:26 +0000 Subject: [PATCH 14/16] fix: restore complete release lockfile From e4c7701fb09ae1989a1015e342b66c1a3fde000c Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Tue, 22 Sep 2026 00:50:07 +0100 Subject: [PATCH 15/16] fix: normalize npm bin path --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b93905d..ed25c8f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "module", "packageManager": "npm@10.9.2", "bin": { - "stellarforge": "./dist/cli.js" + "stellarforge": "dist/cli.js" }, "engines": { "node": ">=22.13.0 <25", From 83052eb538075850be173cc5042d52b9dc119701 Mon Sep 17 00:00:00 2001 From: Ahmed Mahmud Date: Tue, 22 Sep 2026 00:52:28 +0100 Subject: [PATCH 16/16] test: expect normalized npm bin path --- tests/executable.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/executable.test.ts b/tests/executable.test.ts index 1b3f29e..e621f4c 100644 --- a/tests/executable.test.ts +++ b/tests/executable.test.ts @@ -21,7 +21,7 @@ describe('StellarForge CLI executable', () => { bin?: Record; }; - expect(packageJson.bin?.stellarforge).toBe('./dist/cli.js'); + expect(packageJson.bin?.stellarforge).toBe('dist/cli.js'); }); it('preserves the Node.js shebang in the built artifact', () => {