This document covers how to build and publish the gitmesh-agents CLI package to npm.
- Node.js 20+
- pnpm 9.15+
- An npm account with publish access to the
gitmesh-agentspackage - Logged in to npm:
npm login
The fastest way to publish — bumps version, builds, publishes, restores, commits, and tags in one shot:
./scripts/bump-and-publish.sh patch # 0.1.1 → 0.1.2
./scripts/bump-and-publish.sh minor # 0.1.1 → 0.2.0
./scripts/bump-and-publish.sh major # 0.1.1 → 1.0.0
./scripts/bump-and-publish.sh 2.0.0 # set explicit version
./scripts/bump-and-publish.sh patch --dry-run # everything except npm publishThe script runs all 6 steps below in order. It requires a clean working tree and an active npm login session (unless --dry-run). After it finishes, push:
git push && git push origin v<version>If you prefer to run each step individually:
# Bump version
./scripts/version-bump.sh patch # 0.1.0 → 0.1.1
# Build
./scripts/build-npm.sh
# Preview what will be published
cd cli && npm pack --dry-run
# Publish
cd cli && npm publish --access public
# Restore dev package.json
mv cli/package.dev.json cli/package.json./scripts/version-bump.sh <patch|minor|major|X.Y.Z>This updates the version in two places:
cli/package.json— the source of truthcli/src/index.ts— the Commander.version()call
Examples:
./scripts/version-bump.sh patch # 0.1.0 → 0.1.1
./scripts/version-bump.sh minor # 0.1.0 → 0.2.0
./scripts/version-bump.sh major # 0.1.0 → 1.0.0
./scripts/version-bump.sh 1.2.3 # set explicit version./scripts/build-npm.shThe build script runs five steps:
- Forbidden token check — scans tracked files for tokens listed in
.git/hooks/forbidden-tokens.txt. If the file is missing (e.g. on a contributor's machine), the check passes silently. The script never prints which tokens it's searching for. - TypeScript type-check — runs
pnpm -r typecheckacross all workspace packages. - esbuild bundle — bundles the CLI entry point (
cli/src/index.ts) and all workspace package code (@gitmesh/*) into a single file atcli/dist/index.js. External npm dependencies (express, postgres, etc.) are kept as regular imports. - Generate publishable package.json — replaces
cli/package.jsonwith a version that has real npm dependency ranges instead ofworkspace:*references (see package.dev.json below). - Summary — prints the bundle size and next steps.
To skip the forbidden token check (e.g. in CI without the token list):
./scripts/build-npm.sh --skip-checksSee what npm will publish:
cd cli && npm pack --dry-runcd cli && npm publish --access publicAfter publishing, restore the workspace-aware package.json:
mv cli/package.dev.json cli/package.jsongit add cli/package.json cli/src/index.ts
git commit -m "chore: bump version to X.Y.Z"
git tag vX.Y.ZDuring development, cli/package.json contains workspace:* references like:
{
"dependencies": {
"@gitmesh/server": "workspace:*",
"@gitmesh/data": "workspace:*"
}
}These tell pnpm to resolve those packages from the local monorepo. This is great for development but npm doesn't understand workspace:* — publishing with these references would cause install failures for users.
The build script solves this with a two-file swap:
- Before building:
cli/package.jsonhasworkspace:*refs (the dev version). - During build (
build-npm.shstep 4):- The dev
package.jsonis copied topackage.dev.jsonas a backup. generate-npm-package-json.mjsreads every workspace package'spackage.json, collects all their external npm dependencies, and writes a newcli/package.jsonwith those real dependency ranges — noworkspace:*refs.
- The dev
- After publishing: you restore the dev version with
mv package.dev.json package.json.
The generated publishable package.json looks like:
{
"name": "gitmesh-agents",
"version": "0.1.0",
"bin": { "gitmesh-agents": "./dist/index.js" },
"dependencies": {
"express": "^5.1.0",
"postgres": "^3.4.5",
"commander": "^13.1.0"
}
}package.dev.json is listed in .gitignore — it only exists temporarily on disk during the build/publish cycle.
The CLI is a monorepo package that imports code from @gitmesh/server, @gitmesh/data, @gitmesh/core, and several adapter packages. These workspace packages don't exist on npm.
esbuild bundles all workspace TypeScript code into a single dist/index.js file (~250kb). External npm packages (express, postgres, zod, etc.) are left as normal import statements — they get installed by npm when a user runs npx gitmesh-agents onboard.
The esbuild configuration lives at cli/esbuild.config.mjs. It automatically reads every workspace package's package.json to determine which dependencies are external (real npm packages) vs. internal (workspace code to bundle).
The build process includes the same forbidden-token check used by the git pre-commit hook. This catches any accidentally committed tokens before they reach npm.
- Token list:
.git/hooks/forbidden-tokens.txt(one token per line,#comments supported) - The file lives inside
.git/and is never committed - If the file is missing, the check passes — contributors without the list can still build
- The script never prints which tokens are being searched for
- Matches are printed so you know which files to fix, but not which token triggered it
Run the check standalone:
pnpm check:tokens| Script | Command | Description |
|---|---|---|
bump-and-publish |
pnpm bump-and-publish <type> |
One-command bump + build + publish + commit + tag |
build:npm |
pnpm build:npm |
Full build (check + typecheck + bundle + package.json) |
version:bump |
pnpm version:bump <type> |
Bump CLI version |
check:tokens |
pnpm check:tokens |
Run forbidden token check only |