New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@dirstack/kodeks

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dirstack/kodeks

Shared linter configurations and release tooling for TypeScript projects.

latest
Source
npmnpm
Version
1.9.2
Version published
Maintainers
1
Created
Source

Kodeks

npm version license

Shared linter configurations and release tooling for TypeScript projects.

Kodeks provides reusable, opinionated configurations for maintaining consistent code quality across TypeScript projects, plus a kodeks CLI for publishing public packages from a private monorepo.

Usage

oxlint

bun add -d @dirstack/kodeks oxlint

Create a .oxlintrc.json file in your project root:

{
  "extends": ["./node_modules/@dirstack/kodeks/configs/oxlint-base.json"]
}

For Next.js projects, use the Next.js preset:

{
  "extends": ["./node_modules/@dirstack/kodeks/configs/oxlint-next.json"]
}

For TanStack projects, use the TanStack preset:

{
  "extends": ["./node_modules/@dirstack/kodeks/configs/oxlint-tanstack.json"]
}

oxfmt

bun add -d @dirstack/kodeks oxfmt

Since oxfmt does not support configuration inheritance, you have two options:

Option 1: Reference via CLI flag

Add to your package.json scripts:

{
  "scripts": {
    "format": "oxfmt --config ./node_modules/@dirstack/kodeks/configs/oxfmt.json --write ."
  }
}

Option 2: Copy configuration locally

cp ./node_modules/@dirstack/kodeks/configs/oxfmt.json ./.oxfmtrc.json

Commitlint

bun add -d @dirstack/kodeks @commitlint/cli @commitlint/config-conventional

Create a commitlint.json file in your project root:

{
  "extends": ["./node_modules/@dirstack/kodeks/configs/commitlint.json"]
}

Semantic Release

bun add -d @dirstack/kodeks semantic-release conventional-changelog-conventionalcommits

Create a release.json file in your project root:

{
  "extends": "@dirstack/kodeks/semantic-release"
}

A dry-run variant is also available for CI validation:

{
  "extends": "@dirstack/kodeks/semantic-release-dry-run"
}

Lefthook

bun add -d @dirstack/kodeks lefthook

Create a lefthook.json file in your project root and extend the hooks you need:

{
  "extends": [
    "node_modules/@dirstack/kodeks/configs/lefthook-oxlint.json",
    "node_modules/@dirstack/kodeks/configs/lefthook-oxfmt.json",
    "node_modules/@dirstack/kodeks/configs/lefthook-commitlint.json"
  ]
}

Available hooks:

  • lefthook-oxlint.json — Lints and fixes staged files with oxlint (pre-commit)
  • lefthook-oxfmt.json — Formats staged files with oxfmt (pre-commit)
  • lefthook-commitlint.json — Validates commit messages (commit-msg)
  • lefthook-typescript.json — Type checks with tsc (pre-commit)

Publishing public packages from a private monorepo

npm only attests provenance for packages published from a public repository. A private monorepo that ships public client packages therefore publishes them from a public mirror repository. Kodeks provides the commands and reusable GitHub workflows for that setup.

How it flows

  • Version (monorepo). A pushed changeset opens or updates a "Version Packages" pull request. Merging it bumps the versions and writes the changelogs on main.
  • Sync (monorepo to mirror). The version bump on main runs kodeks sync-public, which copies the listed packages and root files into a checkout of the mirror and pushes the result.
  • Release (mirror). The sync commit runs the mirror's release workflow. It builds every package and runs kodeks publish, which publishes each version that is not on npm yet and creates its GitHub Release.

kodeks publish skips versions that are already on npm, so a sync that changes no version publishes nothing. The dist-tag follows the version: 1.2.3 publishes under latest, 1.2.3-beta.0 under beta and is marked as a GitHub prerelease. A stable version below the current latest, such as a 1.0.1 backport after 2.0.0, publishes under latest-1, so latest never moves backwards. kodeks publish passes --provenance, so a publish that cannot attest provenance fails instead of succeeding without it.

The releaseTag setting picks the release tags. With "version", one release tagged v<version> lists every package, and kodeks publish stops before publishing when the packages have different versions. With "package", each package gets its own release tagged <name>@<version>. The default is "version" for one package and "package" for several.

A re-run recovers a release that failed after its packages reached npm. For a version that is already on npm, kodeks publish reads the commit npm recorded at publish time (gitHead). It creates the missing release at that commit only when the commit belongs to the mirror. A version published by hand, or by an earlier setup, gets no release.

Setup in the monorepo

bun add -d @dirstack/kodeks @changesets/cli

Each public package publishes from its built dist directory. Run kodeks prepare-package after the bundler, so dist/package.json points at the built files:

{
  "scripts": {
    "build": "tsdown && kodeks prepare-package"
  }
}

kodeks prepare-package [dir] rewrites the entry points from ./src/*.ts to ./*.js and ./*.d.ts, including nested conditions such as react-server. It points main, module and types at the root export's built files, and drops them when the package has no root export. It also replaces workspace: ranges with the sibling package versions, removes files, scripts, devDependencies and publishConfig.directory, and copies the package's own README.md and LICENSE into dist.

Create a kodeks.release.json in the repository root. The commands read their defaults from this file, and command-line flags override them:

{
  "packages": ["core", "sdk", "react"],
  "releaseTag": "version",
  "sync": {
    "packages": ["core", "sdk", "react", "tsconfig"],
    "licenseFrom": "packages/sdk/LICENSE",
    "extra": ["scripts/mirror/root:."]
  }
}
FieldDefaultDescription
packagesDir"packages"Directory that holds the package directories, in both repositories.
packagesPackage directories to publish, in publish order.
releaseTag"version" for one package, "package" for several"version" tags one v<version> release for packages released in lockstep. "package" tags one <name>@<version> release per package.
sync.packagespackagesPackage directories to mirror. Add shared private packages the public ones build with, such as a tsconfig package.
sync.licenseFromFile copied to the mirror's root LICENSE.
sync.extraFiles or directories copied into the mirror, as "source" (same path) or "source:destination". A directory merges into the destination. Files git ignores inside a directory are skipped, and an extra file that git ignores is an error.
sync.excludeNames skipped at any depth, on top of dist, node_modules and .turbo.

Keep the mirror's root files in the monorepo and list them in sync.extra. In the example above, scripts/mirror/root/ holds the mirror's README.md and package.json. The mirror's package.json builds the public packages and needs Kodeks:

{
  "name": "acme-js",
  "private": true,
  "type": "module",
  "workspaces": ["packages/*"],
  "packageManager": "bun@1.3.14",
  "scripts": {
    "build": "bun run --filter '@acme/core' --filter '@acme/sdk' --filter '@acme/react' build"
  },
  "devDependencies": {
    "@dirstack/kodeks": "^1.9.0",
    "tsdown": "^0.22.3",
    "typescript": "^6.0.3"
  }
}

On every sync, kodeks sync-public replaces the mirror's packages directory with the package files git does not ignore, so a local .env never reaches the mirror. It only runs in a git repository, and stops with an error when git ls-files fails. It also writes a .gitignore and a kodeks.release.json that only holds the packages to publish and the releaseTag. Extras are copied last, so an extra can replace either generated file. Root files that were removed from the monorepo stay in the mirror until you delete them there.

The mirror keeps its own bun.lock, because the monorepo's lockfile describes private packages. The sync workflow refreshes it after each sync with bun install --lockfile-only --ignore-scripts, which resolves only what changed and installs nothing. The release then installs with --frozen-lockfile, so every publish builds with the versions committed in the mirror. After a local kodeks sync-public, run that same command in the mirror before you commit.

Changesets

The Changesets config has no extends, so Kodeks ships a template instead:

bunx kodeks changesets init

This writes .changeset/config.json with two settings for this setup:

  • "privatePackages": { "version": false, "tag": false } keeps every private workspace package out of versioning, so there is no ignore list to maintain.
  • "changelog": "@dirstack/kodeks/changesets-changelog" writes changelog entries without commit hashes or pull request links, which would point at the private monorepo. The changelog needs Node 22.12 or newer.

To release packages in lockstep, list them in fixed and set "releaseTag": "version" in kodeks.release.json, so they share one v<version> GitHub Release. To publish prereleases, run bunx changeset pre enter beta in the monorepo and commit .changeset/pre.json. Run bunx changeset pre exit to return to stable versions.

Workflows

The monorepo calls the version and sync workflows. Pin them to a Kodeks release tag:

# .github/workflows/version.yml
name: Version

on:
  push:
    branches: [main]

concurrency:
  group: version
  cancel-in-progress: false

jobs:
  version:
    permissions:
      contents: write
      pull-requests: write
    uses: dirstack/kodeks/.github/workflows/public-version.yml@<release commit SHA> # v<version>
# .github/workflows/sync-public.yml
name: Sync public mirror

on:
  push:
    branches: [main]
    paths:
      - "packages/core/**"
      - "packages/sdk/**"
      - "packages/react/**"
      - "packages/tsconfig/**"
      - "scripts/mirror/root/**"
      - "kodeks.release.json"
  workflow_dispatch:

concurrency:
  group: sync-public
  cancel-in-progress: false

jobs:
  sync:
    permissions:
      contents: read
    uses: dirstack/kodeks/.github/workflows/public-sync.yml@<release commit SHA> # v<version>
    with:
      mirror-repository: acme/acme-js
    secrets:
      mirror-deploy-key: ${{ secrets.MIRROR_DEPLOY_KEY }}

The sync workflow runs the @dirstack/kodeks range from the monorepo's root package.json, without installing the monorepo's dependencies. Inputs: mirror-repository (required), mirror-branch (default main), kodeks-version, commit-message, and lockfile-command (empty skips the lockfile refresh). The version workflow accepts bun-version, install-command, version-command (default bunx changeset version) and title.

GitHub starts no workflows for a pull request opened with the default GITHUB_TOKEN, so CI does not run on the "Version Packages" pull request. To run CI on it, pass a GitHub App token or a fine-grained token as the github-token secret of the version workflow.

The mirror calls the release workflow from its own .github/workflows/release.yml:

# .github/workflows/release.yml in the mirror
name: Release

on:
  push:
    branches: [main]
  workflow_dispatch:

concurrency:
  group: release
  cancel-in-progress: false

jobs:
  release:
    permissions:
      contents: write
      id-token: write
    uses: dirstack/kodeks/.github/workflows/public-release.yml@<release commit SHA> # v<version>

The release workflow installs npm 11 (input npm-version), runs install-command (default bun install --frozen-lockfile) and build-command (default bun run build), then runs kodeks publish. Commit this file to the mirror once, by hand.

Pin each uses: to the commit SHA of a kodeks release, with the version in a comment, never to the tag: the sync receives the mirror's deploy key and the release holds id-token: write, so a moved tag could push to the mirror or publish to npm. Every action inside these workflows is pinned to a commit SHA as well, so the monorepo and the mirror can turn on Require actions to be pinned to a full-length commit SHA (Settings, Actions, General).

Trusted publishing on npmjs.com

kodeks publish authenticates with npm trusted publishing (OIDC), so the mirror needs no NPM_TOKEN. npm attests provenance for each publish. Trusted publishing needs npm 11.5.1 or newer, and kodeks publish stops with an error when the npm in PATH is older.

npm validates the calling workflow, so the reusable release workflow works as long as the trusted publisher names the mirror's own workflow file:

  • Create each package. npm cannot attach a trusted publisher to a package that does not exist. Publish the first version of each package by hand, from the monorepo, after npm login: build it, then run npm publish --access public inside its dist directory. Add --tag beta to make that first version a beta.
  • Add the trusted publisher. On npmjs.com, open each package's Settings, find Trusted Publisher, and choose GitHub Actions. Enter the mirror's organization and repository, and the workflow filename release.yml (the caller, not public-release.yml). Leave the environment empty. Allow the npm publish action.
  • Lock it down. Once a release has gone through, set each package's publishing access to Require two-factor authentication and disallow tokens.

Sync credential

The sync job pushes to the mirror with a deploy key. A push made with a deploy key starts the mirror's release workflow, which a push made with GITHUB_TOKEN would not.

  • Generate a key pair: ssh-keygen -t ed25519 -N "" -C "mirror sync" -f mirror-deploy-key.
  • In the mirror, open Settings, then Deploy keys, and add mirror-deploy-key.pub with Allow write access.
  • In the monorepo, open Settings, then Secrets and variables, then Actions, and add the contents of mirror-deploy-key as the secret MIRROR_DEPLOY_KEY.
  • Delete both key files from your machine.

Command reference

kodeks prepare-package [dir]
kodeks publish [--packages a,b] [--dry-run]
kodeks sync-public --target <dir> [--packages <list>] [--license-from <path>] [--extra <path>...]
kodeks changesets init [--force]

Run kodeks <command> --help for details. Run kodeks publish --dry-run in the mirror to print the publish and release decisions without publishing. kodeks publish creates releases through the GitHub REST API with GITHUB_TOKEN (or GH_TOKEN). The repository comes from GITHUB_REPOSITORY or the origin remote.

Agentic Coding

Skills

This package includes agent skills for code formatting, testing, and commit conventions.

Available skills:

  • formatting - Code formatting rules not handled by auto-formatters (arrow functions, exports, types, naming, comments, JSX)
  • testing - Best practices, structure, coverage categories, and formatting conventions for writing tests
  • commits - Conventional commit types, scopes, and message formatting guidelines
  • dependencies - Safely upgrade project dependencies to their latest versions across workspaces
  • dhh - DHH-style code review for TypeScript/React - direct, opinionated, allergic to over-engineering

Install all skills using skills.sh:

npx skills add dirstack/kodeks

Or install individual skills:

npx skills add dirstack/kodeks --skill formatting
npx skills add dirstack/kodeks --skill testing
npx skills add dirstack/kodeks --skill commits

FAQs

Package last updated on 13 Oct 2026

Related posts