npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

projen-worktrunk

v0.1.8

Published

A projen component for authoring Worktrunk project configuration, so the hooks that run across a worktree's lifecycle are declared in your projenrc along with everything else.

Readme

projen-worktrunk

A projen component for authoring Worktrunk project configuration, so the hooks that run across a worktree's lifecycle are declared in your projenrc along with everything else.

Worktrunk ships as a binary and is not on npm: install it separately. Checked against Worktrunk 0.72.0, and its documentation and source at 0.78.0.

Usage

Install dependencies:

npm install -D projen-worktrunk

Then, create a Worktrunk component for your root project:

import { Project } from 'projen'
import { Worktrunk, pipeline } from 'projen-worktrunk'

const project = new Project({
  name: 'my-project',
})

new Worktrunk(project, {
  config: {
    // A single command.
    'pre-start': 'pnpm install --frozen-lockfile',

    // Named commands, run concurrently.
    'post-start': {
      copy: 'wt step copy-ignored',
      server: 'wt step tether -- pnpm dev --port {{ branch | hash_port }}',
    },

    // Steps run in turn; the commands within a step run concurrently.
    'pre-merge': pipeline({ lint: 'pnpm lint' }, { test: 'pnpm test' }),

    list: {
      url: 'http://localhost:{{ branch | hash_port }}',
    },
  },
})

This synthesizes .config/wt.toml. Worktrunk reads it from the root of a worktree, so the component refuses a subproject unless you name a filename.

Hooks

config takes the ten hook events — pre- and post- × switch, start, commit, merge and remove — each in one of the three forms Worktrunk tells apart by shape. The component never turns one form into another: the form decides the text a teammate is asked to approve, and the name a command is addressed by (wt hook pre-merge project:test). Prefer named commands; a single command has no name to address.

Hooks are written in lifecycle order, though TOML requires single commands to come ahead of every table. Named commands keep the order you declare them in.

Worktrunk shell-escapes template variables itself, so don't quote {{ branch }}.

Alongside the hooks, config takes aliases (in the same three forms), commit.generation.template-append, forge, list.url and step.copy-ignored.exclude. Worktrunk publishes no schema, so these are mirrored by hand. For a key that is missing, reach for the file:

worktrunk.file.addOverride('list.url', 'http://localhost:3000')

Contributing hooks

Other components can add to the hooks a project declares:

worktrunk.addHook('pre-start', 'pnpm install')
worktrunk.addCommand('pre-merge', 'test', 'pnpm test')
worktrunk.addStep('post-start', { build: 'pnpm build' })

Each throws rather than replace a command or change a hook's form.

Approval

Worktrunk asks each teammate to approve a project's commands before it first runs them. It remembers approvals per project by the exact text of the command: renaming a command, or moving it to another event, asks nothing, and editing it asks again for that command alone.

So keep the approved text stable, and let what it runs evolve under ordinary review:

'pre-merge': { test: 'pnpm run wt:pre-merge' }

A hook that inlines its logic re-prompts the whole team on every tweak, which trains everyone to approve without reading.

Declining approval skips every project command for that operation and carries on, so a pre-* hook should not assume an earlier one ran.

The component rejects a command that gets past the prompt: --yes or -y on any wt invocation, or a reach for approvals.toml. Such a command needs approving once itself, and from then on runs every later command unreviewed on each teammate's machine. It is a guard rail, not a sandbox — it cannot see into a script a hook calls. Pass allowApprovalBypass: true to opt out.

For the same reason the config is kept out of linguist-generated, so its diffs arrive expanded in review.

Checking hooks

Two tasks, neither of which needs approval or joins another task:

  • worktrunk:show prints the hooks as Worktrunk resolves them.
  • worktrunk:dry-run expands every configured hook without running it, and fails on a template Worktrunk cannot expand — a syntax error or an undefined variable. {{ branch }} is undefined in a detached worktree; guard it with {% if branch %}.

Pass tasks: false to leave them out.

Mistakes caught at synthesis

Worktrunk fails quietly on each of these, so the component throws instead:

  • an unknown event, such as pre-started, which Worktrunk warns about and then ignores, so the hook never runs
  • an empty table, pipeline or step, which it reads as no hook at all, and an empty command, which it asks approval to run
  • a : in a command name, over which it refuses to load the whole file
  • a template tag that is opened and never closed

Adopting an existing config

Worktrunk has no way to include one config in another, so the component owns the whole file. Where a .config/wt.toml exists that projen did not generate, synthesis fails rather than replace it:

  1. Move its contents into config.
  2. git rm .config/wt.toml, then synthesize.
  3. Compare the result, and run worktrunk:dry-run.

Pass overwriteExisting: true to replace the file instead. Unchanged commands keep their approvals either way.

Ignore files

A deny-by-default .gitignore (.*) excludes .config/, and git does not descend into an excluded directory, so the config would be generated and never committed. The component re-includes each dot-directory leading to the file (!/.config), leaving dotfiles inside it ignored. Other files in .config/ become visible to git. Pass gitignore: false if you manage this yourself.