mise-lib-template is a language-agnostic mise-powered [1] template for building library/executable projects. It uses GCP Artifact Registry for publishing generic packages by default, but can be easily adapted for npm, PyPI, NuGet, CodeArtifact, etc.
Here's what this template gives you off the bat:
- A language-agnostic self-documenting command interface via
mise— keep all your project tasks, tool versions, and environment config in onemise.toml! - Cross-platform environment management - mise installs any dev-tools your project defines
- CI/CD with GitHub Actions - run test on MR commits, tag and release on merges to main.
- Easy CI/CD customization - simply modify mise tasks that hook into actions
- Trunk-based development and automated versioning with conventional commits - just on feature branches and merge to main for bumps, semantic-release will handle version bumping for you!
- GCP Artifact Registry publishing (easily modified for other registries)
- bash 3.2+
- mise — manages tools, tasks, and environment
To install mise, run:
❯ curl https://mise.jdx.dev/install.sh | sh| Template | When to use | Language | Registry |
|---|---|---|---|
| agnostic | Any language — fill in your own tasks | Any | GCP Artifact Registry |
| uv | Python library or CLI | Python 3.12+ | PyPI |
| zig | Zig library or binary with cross-platform builds | Zig 0.16.x | GitHub Releases |
| pnpm | TypeScript library published to npm | TypeScript / Node.js LTS | npm |
All tools are installed automatically by mise — you do not need to install Python or Zig separately.
Scaffold a new project:
# Click "Use this template" on GitHub, then:
❯ git clone <your-new-repo>
❯ cd <your-new-repo>
❯ bash mise-tasks/scaffold --project your-project-name --template uv # Python
❯ bash mise-tasks/scaffold --project your-project-name --template zig # Zig
❯ bash mise-tasks/scaffold --project your-project-name # agnostic (prompted)Install dependencies and scaffold the template for your needs:
# Install project tools
❯ mise install
# Scaffold project for your language
❯ mise run scaffoldType mise tasks to see all the tasks at your disposal:
❯ mise tasksBuild, run and test with mise run. The template will show TODO messages in console prior to adapting.
❯ mise run run
TODO: Implement build for mise-lib-template@2.x
TODO: Implement run
❯ mise run test
TODO: Implement build for mise-lib-template@2.x
TODO: Implement testMise runs the necessary task dependencies automatically!
Commit using conventional commits (feat:, fix:, docs:). Merge/push to main and CI/CD will run automatically bumping your project version and publishing a package.
The template includes Docker support for running tasks in isolated containers without installing dependencies on your host machine.
Prerequisites:
- Docker Desktop or Docker Engine
Available Docker commands:
❯ mise run docker-build # Build the Docker image
❯ mise run docker-run # Run the project in a container
❯ mise run docker-test # Run tests in a containerThe Dockerfile and docker-compose.yml are configured to install all required dependencies automatically. This is useful for:
- Running tasks without installing tools locally
- Ensuring consistency across different development machines
- Testing in a clean environment
The template includes a pre-configured devcontainer for consistent cross-platform development environments across your team.
Prerequisites on host:
- Docker Desktop or Docker Engine
- An editor with Dev Containers support (e.g. VS Code, Zed, WebStorm, etc.)
Open the project in your editor and select "Reopen in Container". In your terminal you will find everything pre-installed including mise, gcloud and more:
- Git, GitHub CLI, and Google Cloud CLI pre-installed
- Git credentials automatically shared from host via SSH agent forwarding
- Claude CLI credentials mounted from
~/.claude - Docker-in-Docker support for building containers
Authentication:
- Git/GitHub: Automatic via SSH agent forwarding (no setup needed)
- gcloud: Run
gcloud auth logininside the container on first use - Claude: Automatically available if configured on host
Regardless of which template you chose, the same mise run commands work identically:
mise run build # build your project
mise run test # run tests
mise run lint # run static analysis
mise run format # format code
mise run publish # publish to your registrymise run install installs the correct toolchain for your template (Python + uv, Zig, or node for semantic-release).
mise run install # Install project dependencies
mise run build # Build for development
mise run test # Run tests
mise run run # Run locally
mise run clean # Clean build artifactsUse conventional commits for automatic versioning:
git commit -m "feat: add new feature" # Minor bump (0.1.0 → 0.2.0)
git commit -m "fix: resolve bug" # Patch bump (0.1.0 → 0.1.1)
git commit -m "docs: update readme" # No bump
git commit -m "feat!: breaking change" # Major bump (0.1.0 → 1.0.0)Push to main:
git push origin mainCI/CD automatically runs tests, creates a release, and publishes to your configured registry.
New projects start their version history in the 0.x range. The first release is computed from a
seeded v0.0.0 baseline: a feat: commit yields 0.1.0, a fix: yields 0.0.1. The project stays
in 0.x until you intentionally cut 1.0.0. Note: the first breaking change (feat!: /
BREAKING CHANGE:) below 1.0.0 promotes the project straight to 1.0.0 (a semantic-release behaviour).
The mise.toml tasks contain TODO placeholders. Run Claude's /adapt command for guided customization:
claude /adaptOr manually replace placeholders with your language's commands:
# Node.js example
[tasks.install]
run = "npm install"
[tasks.build]
run = "npm run build"
[tasks.test]
depends = ["build"]
run = "npm test"
[tasks.publish]
depends = ["test", "build-prod"]
run = "npm publish"The publish task defaults to GCP Artifact Registry. Edit it in mise.toml for your registry:
# npm
[tasks.publish]
depends = ["test", "build-prod"]
run = "npm publish"
# PyPI
[tasks.publish]
depends = ["test", "build-prod"]
run = "twine upload dist/*"
# Docker
[tasks.publish]
depends = ["test", "build-prod"]
run = "docker push myimage:$VERSION"Configure your mise.toml [env] section accordingly:
# GCP (default)
GCP_REGISTRY_PROJECT_ID = "my-project"
GCP_REGISTRY_REGION = "us-east1"
GCP_REGISTRY_NAME = "my-registry"
# Or use registry-specific variables for npm, PyPI, etc.Configure secrets once at the organization level (Settings → Secrets → Actions):
For GCP (agnostic template, default):
GCP_SA_KEY- Service account JSON keyGCP_REGISTRY_PROJECT_ID,GCP_REGISTRY_REGION,GCP_REGISTRY_NAME
For PyPI (uv template):
UV_PUBLISH_TOKEN— PyPI API token (or configure OIDC trusted publishing)
For GitHub Releases (zig template):
GH_TOKENorGITHUB_TOKEN— already present via GitHub Actions default token
For npm (pnpm template):
NPM_TOKEN— npm access token with publish rights (see npm Setup below)
All projects automatically inherit organization secrets.
The pnpm template publishes to npm. Follow these steps to configure publishing for your project and CI/CD.
Sign up at npmjs.com if you don't have one.
Scoped packages (@your-org/package-name) require an npm organization:
- Go to npmjs.com/org/create
- Choose a name (this becomes your scope, e.g.
@your-org) - Free orgs can publish public scoped packages
To use a scoped name, update "name" in your project's package.json:
{
"name": "@your-org/my-library"
}- Log in to npmjs.com
- Click your avatar → Access Tokens → Generate New Token
- Choose Granular Access Token (recommended) or Classic Token → Automation
- Granular: set expiry, select the specific packages to allow publishing, set permission to Read and write
- Classic Automation: no expiry, grants publish to all packages in the account — simpler but broader scope
- Copy the token — it is only shown once
Per-repository (for a single project):
- Go to your repository → Settings → Secrets and variables → Actions
- Click New repository secret
- Name:
NPM_TOKEN, Value: paste your token - Click Add secret
Organization-level (shared across all repos — recommended):
- Go to your GitHub organization → Settings → Secrets and variables → Actions
- Click New organization secret
- Name:
NPM_TOKEN, Value: paste your token - Set Repository access to All repositories (or select specific ones)
- Click Add secret
All repositories in the org automatically inherit organization-level secrets. This means every project using the pnpm template will be able to publish without configuring secrets per-repo.
npm Granular Access Tokens expire after 90 days by default. Set a calendar reminder to rotate before expiry, or use Trusted Publishing (below) to eliminate tokens entirely.
Classic Automation tokens can be set to no expiry — simpler but grants broader access.
npm supports OIDC-based trusted publishing, letting GitHub Actions publish without storing any token. Set it up once per package after the first manual publish.
Setup (on npmjs.com):
- Publish the package at least once manually first (npm requires the package to exist)
- Go to the package page → Settings → Publishing access → Require two-factor authentication or automation token → switch to Allow publishing from CI/CD with a generated token
- Set:
- Repository owner: your GitHub username or org
- Repository name: your repo name
- Workflow:
release.yml
Update release.yml to use OIDC instead of NPM_TOKEN:
permissions:
contents: write
issues: write
pull-requests: write
id-token: write # ← add this for OIDC
# In the Publish package step, remove NPM_TOKEN from env.
# pnpm publish will authenticate via the OIDC token automatically.Also add --provenance to the publish command in mise.toml:
[tasks.publish]
run = "pnpm publish --access public --no-git-checks --provenance"Once trusted publishing is configured, remove NPM_TOKEN from GitHub secrets — it is no longer needed.
After setting the secret (or configuring trusted publishing), push a feat: or fix: commit to main. CI will:
- Run
mise run upversion— bumps version inpackage.json, creates git tag - Run
mise run publish— callspnpm publish --access public --no-git-checks
Check the Actions tab to confirm both steps succeed.
| Error | Cause | Fix |
|---|---|---|
403 Forbidden |
Token lacks publish rights or wrong package name | Regenerate token with correct scope; verify "name" in package.json |
402 Payment Required |
Scoped package requires paid account or org | Use a free org, or publish unscoped |
ENEEDAUTH |
NPM_TOKEN secret not set or misspelled |
Check GitHub secrets; ensure secret name is exactly NPM_TOKEN |
Cannot publish over existing version |
Version already published | Bump version with a new commit; never re-publish the same version |
OIDC token error |
Trusted publishing misconfigured | Verify repo name, owner, and workflow name match exactly on npmjs.com |
mise tasks provide hooks for overriding CI/CD behavior:
- build-prod: specifies how to create production builds in CI
- test: specifies hot to test your build
- publish: specifies how to publish to your artifact regiistry
Edit these tasks to change how CI/CD runs, but avoid editing .github/workflows/ directly.
To modify semantic versioning behavior or to deviate fromt trunk-based development, modify your .releaserc.json to modify semantic-versioning CLI's behavior.
Claude commands provide guided workflows for complex tasks. The template includes two custom commands, while most workflow commands come from the Claudevoyant plugin (installed via mise run install-claude-plugins).
claude /adapt # Customize template for your language (auto-deletes after use)
claude /upgrade # Migrate to newer template versionclaude /spec:new # Create a new project plan
claude /spec:go # Execute the plan with spec-driven development
claude /dev:docs # Validate documentation
claude /dev:commit # Create conventional commit
claude /dev:review # Perform code reviewWhen a new template version is released:
claude /upgradeThis creates a comprehensive migration plan, compares files, and walks you through changes while preserving your customizations.
- Customize
mise.tomltasks for your language - Write code in
src/ - Add tests
- Configure GitHub organization secrets
- Set up branch protection on
main - Make your first conventional commit
- Push and watch the automated release
Or just run claude /adapt.
See Architecture for implementation details.