This guide is the complete contributor workflow for cloning, installing, running, testing, and developing the project.
- Before You Start
- Development TL;DR
- Environment Requirements
- Install Dependencies
- Run the CLI Locally
- Authentication for Local Development
- Project Layout
- Development Workflow
- Tests and Validation
- Ghost Fixture Workflow
- Coding Guidelines
- Documentation Updates
- Submitting a Pull Request
- Getting Help
- Contributor License Agreement
- Releases
- For small fixes (typos, docs, focused bug fixes), open a PR directly.
- For non-trivial features or CLI behavior changes, open or discuss an issue first.
- Keep changes scoped to a single concern per PR.
nvm use
corepack enable
pnpm install
pnpm dev --help
pnpm lint
pnpm run format:check
pnpm typecheck
pnpm test
pnpm build- Node.js
22.13+,24.x, or26.x(.nvmrcdefaults to24; CI validates all three) corepackenabledpnpm 11.x- A Ghost development or staging site - don’t run a local development build of
ghstagainst a production Ghost instance with data you care about
nvm use
corepack enable
pnpm installIf this is your first time in the repo, verify local tooling:
pnpm lint
pnpm run format:check
pnpm typecheck
pnpm test
pnpm buildRun directly from TypeScript source:
pnpm dev --help
pnpm dev auth status
pnpm dev post listRun built output:
pnpm build
node dist/index.js --helpOptional global link:
pnpm link --global
ghst --helpInteractive:
pnpm dev auth loginNon-interactive:
pnpm dev auth login \
--non-interactive \
--url https://your-site.ghost.io \
--staff-token "{id}:{secret}" \
--jsonConfig and site linkage:
- User config:
~/.config/ghst/config.json - Project config:
.ghst/config.json - Example env:
.env.example
Connection resolution order:
--site--url+--staff-tokenGHOST_URL+GHOST_STAFF_ACCESS_TOKEN.ghst/config.json- active site in user config
- Entrypoint:
src/index.ts - Commands:
src/commands/* - Core libraries:
src/lib/* - Validation schemas:
src/schemas/* - MCP server/tools:
src/mcp/* - Tests:
tests/* - Ghost fixture scripts:
scripts/check-ghost-fixtures.ts,scripts/capture-ghost-fixtures.ts - CI workflows:
.github/workflows/*
- Create a branch from updated
main:
git switch -c <your-branch-name>- Make the smallest change that solves one problem.
- Add or update tests with code changes.
- Run full validation locally.
- Push and open a PR.
Run the same checks as CI:
pnpm lint
pnpm run format:check
pnpm typecheck
pnpm test
pnpm buildUseful variants:
pnpm test:watch
pnpm lint:fix
pnpm format
pnpm format:checkCI reference:
- Workflow:
.github/workflows/ci.yml - Node:
22,24,26 - Package manager:
pnpm 11.x
When changing behavior that depends on Ghost Admin API fixture mocks:
- Run the offline fixture contract check:
pnpm fixtures:ghost:checkImportant notes:
- Fixture-backed tests use committed static JSON only.
pnpm fixtures:ghost:checkis fully offline and should be deterministic.pnpm fixtures:ghost:captureis an optional lightweight local snapshot tool for manual inspection only; it is not part of the normal validation workflow.- Fixture details are documented in
tests/fixtures/ghost-admin/README.md.
- Keep command handlers thin; move behavior to
src/lib. - Validate command input with Zod before network calls.
- Map API/validation failures to
ExitCodeinsrc/lib/errors.ts. - Preserve CLI contract shape:
ghst <resource> <action>. - Do not introduce breaking CLI/interface changes without updating docs and tests.
- Keep changes focused and avoid unrelated refactors in the same PR.
- Update docs whenever command behavior, flags, output, or config behavior changes.
- At minimum, keep
README.mdand this file in sync with the code.
- Ensure your branch is current with
main. - Ensure all local checks pass:
pnpm lint && pnpm run format:check && pnpm typecheck && pnpm test && pnpm build- Push your branch and open a PR against
main. - In your PR description, include what changed, why it changed, how you tested it, and any follow-up work.
PR expectations:
- Include tests for behavior changes.
- Keep PR scope aligned with the issue/intent.
- Respond to review feedback with follow-up commits.
- Open an issue for bugs, regression reports, and feature requests.
- Include reproduction steps, expected behavior, actual behavior, and environment details.
- For implementation questions, include the command(s), flags, and sample output/error.
By contributing your code to Ghost you grant the Ghost Foundation a non-exclusive, irrevocable, worldwide, royalty-free, sublicenseable, transferable license under all of Your relevant intellectual property rights (including copyright, patent, and any other rights), to use, copy, prepare derivative works of, distribute and publicly perform and display the Contributions on any licensing terms, including without limitation: (a) open source licenses like the MIT license; and (b) binary, proprietary, or commercial licenses. Except for the licenses granted herein, You reserve all right, title, and interest in and to the Contribution.
You confirm that you are able to grant us these rights. You represent that You are legally entitled to grant the above license. If Your employer has rights to intellectual property that You create, You represent that You have received permission to make the Contributions on behalf of that employer, or that Your employer has waived such rights for the Contributions.
You represent that the Contributions are Your original works of authorship, and to Your knowledge, no other person claims, or has the right to claim, any right in any invention or patent related to the Contributions. You also represent that You are not legally obligated, whether by entering into an agreement or otherwise, in any way that conflicts with the terms of this license.
The Ghost Foundation acknowledges that, except as explicitly described in this Agreement, any Contribution which you provide is on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
- Package:
@tryghost/ghst - Automatic:
.github/workflows/release.ymlruns every Monday at15:00 UTCand publishes only when there are commits since the last release tag (vX.Y.Z). - Manual: run the
ReleaseGitHub Actions workflow withworkflow_dispatch; setforce=trueto publish even with no new commits. - Versioning: if all commits since the last release tag are by Renovate (
renovate[bot]), the workflow usespatch; otherwise it usesminor. - Prerequisites: npm trusted publishing must be configured for this repository/workflow.