@ls-stack/pkg-manager
v0.11.2
Published
CLI tool for managing package publishing with hash-based change tracking and monorepo support
Readme
pkg-manager
CLI tool for managing package publishing with hash-based change tracking and monorepo support.
Requires Node.js >= 25.0.0 (uses native TypeScript support)
Installation
pnpm add -D @ls-stack/pkg-managerQuick Start
# Initialize configuration
pkg-manager init
# Publish a package
pkg-manager publish
# Publish all changed monorepo packages in dependency order
pkg-manager publish-allCommands
init
Creates a pkg-manager.config.ts configuration file.
pkg-manager init [--force]Options:
--force- Overwrite existing config file
Features:
- Detects monorepo setup (looks for
pnpm-workspace.yaml) - Prompts for pre-publish scripts (lint, test, build)
- Scans
packages/directory for monorepo packages - Allows configuring inter-package dependencies
publish
Publishes a package with hash-based change detection.
pkg-manager publish [package] [--type <type>] [--force] [--dry-run] [--skip-confirm] [--no-push]Arguments:
package- Package name to publish (monorepo only, optional - prompts if not provided)
Options:
--type <type>- Version bump type:patch,minor,major,prerelease,release,prepatch-alpha,preminor-beta,premajor-rc, etc.--force- Publish even if no changes detected--dry-run- Preview what would happen without making changes--skip-confirm- Skip major version confirmation prompt--no-push- Skip pushing the version commit and git tag
Workflow:
- Verifies git working directory is clean
- Prompts for version type if not provided (supports prerelease via two-level select)
- Confirms major version bumps (configurable)
- Builds dependencies first (monorepo, topological order)
- Runs pre-publish scripts (required - see Pre-Publish Scripts)
- Generates a SHA256 hash of the files npm will publish
- Checks hash against stored hashes (prevents duplicate publishes)
- Verifies npm login and offers to run
pnpm npm loginif needed - Bumps version with
pnpm version - Commits the version and generated release files, then creates git tag (
packageName@version) - Publishes with
pnpm publish --access public(uses--tag <preid>for prereleases) - Saves hash for future duplicate detection
- Pushes git commits and tag
- Runs post-publish scripts (if configured)
publish-all
Publishes every changed configured monorepo package in dependency order.
pkg-manager publish-all [--type <type>] [--force] [--dry-run] [--skip-confirm] [--no-push]The default version bump is patch. Packages whose publish hash has not changed
are skipped. When a dependency is released, configured dependents are released
after it even if their own files were unchanged, ensuring their published
workspace dependency metadata resolves to the new version. Explicit semver
dependency ranges are advanced while workspace:, catalog:, *, and
latest ranges are preserved.
The short command is pkg-manager pa.
Configuration
Configuration is optional and stored in pkg-manager.config.ts:
import { defineConfig } from '@ls-stack/pkg-manager'
export default defineConfig({
requireMajorConfirmation: true,
prePublish: [
{ command: 'pnpm lint', label: 'Linting' },
{ command: 'pnpm test', label: 'Testing' },
{ command: 'pnpm build', label: 'Building' },
],
monorepo: {
packages: [
{ name: '@scope/core', path: 'packages/core' },
{
name: '@scope/utils',
path: 'packages/utils',
dependsOn: ['@scope/core'],
},
{
name: '@scope/native',
path: 'packages/native',
release: { type: 'napi', npmDir: 'npm' },
},
],
},
})Options
| Option | Type | Default | Description |
| ------------------------------- | ---------- | --------------------------------------- | --------------------------------------- |
| prePublish | array | Uses pre-publish script | Scripts to run before publishing |
| prePublish[].command | string | Required | Command to execute |
| prePublish[].label | string | Required | Display label for the script |
| postPublish | array | - | Scripts to run after publishing |
| postPublish[].command | string | Required | Command to execute |
| postPublish[].label | string | Required | Display label for the script |
| monorepo | object | - | Monorepo configuration |
| monorepo.packages | array | Required | List of packages |
| monorepo.packages[].name | string | Required | Package name (from package.json) |
| monorepo.packages[].path | string | Required | Path to package directory |
| monorepo.packages[].dependsOn | string[] | [] | Package names this depends on |
| monorepo.packages[].release | object | - | Specialized package release lifecycle |
| release.type | 'napi' | Required | Publish an N-API package as one unit |
| release.npmDir | string | 'npm' | Generated platform package directory |
| hashStorePath | string | node_modules/.pkg-manager/hashes.json | Where to store publish hashes |
| requireMajorConfirmation | boolean | true | Require confirmation for major versions |
| gitPush | boolean | true | Push git commits and tag after publish |
Pre-Publish Scripts
Pre-publish scripts are required. They ensure your package is built and validated before publishing.
Resolution order:
- If
prePublishis configured inpkg-manager.config.ts, those scripts are used - Otherwise, looks for a
pre-publishscript inpackage.json - If neither exists, the publish command exits with an error
Simplest setup - add a pre-publish script to your package.json:
{
"scripts": {
"pre-publish": "pnpm lint && pnpm build"
}
}This works without any config file.
N-API Packages
An N-API package normally consists of a JavaScript loader plus one npm package per operating system and CPU target. Configure it once and pkg-manager presents the root package as a single publish choice:
export default defineConfig({
monorepo: {
packages: [
{
name: '@scope/native',
path: 'native',
release: { type: 'napi', npmDir: 'npm' },
},
],
},
})The package's pre-publish script must build or collect every configured target
artifact into npmDir. With @napi-rs/cli, a typical script is:
{
"scripts": {
"create-npm-dirs": "napi create-npm-dirs --npm-dir npm",
"collect-artifacts": "napi artifacts --output-dir artifacts --npm-dir npm",
"pre-publish": "pnpm test && pnpm create-npm-dirs && pnpm collect-artifacts"
}
}Map the generated platform package names to their local directories with pnpm
overrides. This lets pkg-manager update the frozen lockfile before the new
platform versions exist on npm:
overrides:
'@scope/native-darwin-arm64': link:./native/npm/darwin-arm64
'@scope/native-linux-x64-gnu': link:./native/npm/linux-x64-gnu
# Add every configured target.After the version bump, pkg-manager synchronizes every generated platform manifest, validates that all artifacts exist, and updates the root optional dependencies and workspace lockfile. It then commits and tags the synchronized release, publishes the platform packages, and finally publishes the root loader. Prereleases apply the same npm dist-tag to both the platform packages and the root package.
Post-Publish Scripts
Post-publish scripts are optional. They run after a successful publish, hash save, git commit, and push. Useful for deploy steps, notifications, or cleanup.
export default defineConfig({
postPublish: [
{ command: 'pnpm deploy', label: 'Deploying' },
{ command: 'node notify.ts', label: 'Sending notification' },
],
})Prerelease Versions
pkg-manager supports publishing prerelease versions (alpha, beta, rc).
Interactive mode uses a two-level select:
- For stable versions (e.g.,
1.2.3): the first select showspatch,minor,major, andprerelease.... Choosingprerelease...opens a second select with all combinations ofprepatch/preminor/premajor×alpha/beta/rc. - For prerelease versions (e.g.,
1.2.4-alpha.0): relevant options are promoted to the top-level select —prerelease(bump number),graduate to beta/rc, andrelease(drop the prerelease suffix).
CLI mode:
# Start a prerelease cycle
pkg-manager publish --type prepatch-alpha # 1.2.3 → 1.2.4-alpha.0
pkg-manager publish --type preminor-beta # 1.2.3 → 1.3.0-beta.0
pkg-manager publish --type premajor-rc # 1.2.3 → 2.0.0-rc.0
# Bump existing prerelease
pkg-manager publish --type prerelease # 1.2.4-alpha.0 → 1.2.4-alpha.1
# Release from prerelease
pkg-manager publish --type release # 1.2.4-alpha.1 → 1.2.4Prerelease versions are published with --tag <preid> (e.g., --tag alpha) so they don't become the latest dist-tag on npm.
Hash-Based Change Detection
pkg-manager generates a SHA256 hash of the files reported by npm pack --dry-run
before publishing. N-API releases also include the generated platform package
directory in the hash, so a native binary change is detected even when the root
JavaScript loader is unchanged. The hash is stored locally and checked on
subsequent publishes to prevent publishing identical builds.
Hashes are stored in node_modules/.pkg-manager/hashes.json by default (not committed to git).
Use --force to bypass hash checking when needed.
Monorepo Support
For monorepos, pkg-manager:
- Detects monorepo setup via
pnpm-workspace.yaml - Scans
packages/for package.json files - Builds dependencies in topological order before the target package
- Runs pre-publish scripts with
pnpm --filter <package>in monorepo mode
Dependency Order
If package B depends on package A (dependsOn: ["@scope/a"]), publishing B will first build A to ensure B has the latest dependency code.
Examples
# Initialize with default settings
pkg-manager init
# Re-initialize, overwriting existing config
pkg-manager init --force
# Publish with interactive prompts
pkg-manager publish
# Publish a specific package with patch version
pkg-manager publish @scope/utils --type patch
# Preview publish without making changes
pkg-manager publish --dry-run
# Force publish even if unchanged
pkg-manager publish --force --type patch
# Publish major version without confirmation
pkg-manager publish --type major --skip-confirm
# Start a prerelease cycle
pkg-manager publish --type prepatch-alpha
# Bump an existing prerelease
pkg-manager publish --type prerelease
# Release from a prerelease version
pkg-manager publish --type releaseLicense
MIT
