<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: sumbad</title>
    <description>The latest articles on DEV Community by sumbad (@sumbad).</description>
    <link>https://dev.to/sumbad</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192325%2F1a65a2ca-1822-4f39-9a93-e198512e6bba.jpeg</url>
      <title>DEV Community: sumbad</title>
      <link>https://dev.to/sumbad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sumbad"/>
    <language>en</language>
    <item>
      <title>Local package manager</title>
      <dc:creator>sumbad</dc:creator>
      <pubDate>Fri, 26 Jun 2026 07:17:39 +0000</pubDate>
      <link>https://dev.to/sumbad/local-package-manager-2feo</link>
      <guid>https://dev.to/sumbad/local-package-manager-2feo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — Developing an npm package and testing it inside a host project usually means a slow loop of draft releases, or fighting with &lt;code&gt;npm link&lt;/code&gt;. I built &lt;a href="https://github.com/sumbad/kley" rel="noopener noreferrer"&gt;&lt;strong&gt;kley&lt;/strong&gt;&lt;/a&gt;: a small local package manager written in Rust. A local registry, safe-by-default installs — two core commands &lt;code&gt;publish&lt;/code&gt; and &lt;code&gt;install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who this is for:&lt;/strong&gt; if you maintain JS/TS libraries, juggle several Node.js versions with &lt;code&gt;nvm&lt;/code&gt;, and are tired of publishing throwaway versions just to test them — this is for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem: the draft-release loop
&lt;/h2&gt;

&lt;p&gt;Modern JS/TS development is, to a large extent, juggling npm packages. Need an HTTP call? &lt;code&gt;axios&lt;/code&gt;. Environment variables? &lt;code&gt;dotenv&lt;/code&gt;. Unique IDs? &lt;code&gt;uuid&lt;/code&gt;. And splitting your own code into packages has become the norm — for reuse, for separation of concerns, for sharing across teams.&lt;/p&gt;

&lt;p&gt;Things get harder when you don't just &lt;em&gt;use&lt;/em&gt; a library but &lt;em&gt;develop&lt;/em&gt; one. Sometimes you can work on it in isolation. But often you can't — when the library is tightly coupled to its runtime context, when it's an SDK, or when test coverage isn't enough to trust on its own.&lt;/p&gt;

&lt;p&gt;So we fall back on a familiar ritual: cut a draft/pre-release version, push it, wait for CI, publish to the remote registry, then pull and install it in the host project to see if it actually works. Then change one line and do it all again.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 I always validate a library in at least one real consuming project before a stable release, even when tests pass. Front-end and back-end builds almost always involve transpilation and bundling — code that's correct in tests can behave differently after the host project's build.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This loop inflates TTM (time to market), burns CI minutes, and when you're cutting &lt;em&gt;many&lt;/em&gt; drafts, the overhead multiplies. Push → CI → publish → download → install. Round and round.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqpagqaoiqlvp8wislwk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqpagqaoiqlvp8wislwk.png" alt="Diagram showing the slow draft-release loop: code push → CI → npm publish → download → npm install" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  "Just use &lt;code&gt;npm link&lt;/code&gt;" — except it barely holds up
&lt;/h2&gt;

&lt;p&gt;There's a built-in answer: &lt;code&gt;npm link&lt;/code&gt;. It symlinks your library into the host project, no remote publishing needed. Great — when it fits. But it's narrow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It's tied to one Node.js version.&lt;/strong&gt; With &lt;code&gt;nvm&lt;/code&gt;, the link is created for the &lt;em&gt;current&lt;/em&gt; node version's global modules. If your host project runs Node 18 and the library was linked under Node 20 — the link is simply invisible. Worse, &lt;code&gt;npm link&lt;/code&gt; doesn't error when this happens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linking multiple packages clobbers earlier links.&lt;/strong&gt; &lt;code&gt;npm link B&lt;/code&gt; reinstalls all other deps and quietly drops your existing link to &lt;code&gt;A&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate dependencies.&lt;/strong&gt; The library resolves its own deps from its own folder — so if both the app and the library use React, you can end up with two Reacts in the bundle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to footgun&lt;/strong&gt; — e.g. accidentally linking a global package because of a typo in the name.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;npm unlink&lt;/code&gt; is its own special hell.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And any &lt;code&gt;npm install&lt;/code&gt; after linking wipes the link, so you re-link again.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Then &lt;code&gt;npm install &amp;lt;path&amp;gt;&lt;/code&gt;" — better, still not it
&lt;/h2&gt;

&lt;p&gt;Installing from a local path is closer, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It installs &lt;em&gt;all&lt;/em&gt; the library's dependencies&lt;/strong&gt;, including &lt;code&gt;devDependencies&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can't really run offline&lt;/strong&gt; — even with zero deps, npm still hits the network to validate.&lt;/li&gt;
&lt;li&gt;Since npm 5 it also creates symlinks — but they &lt;strong&gt;point to the library's *source directory&lt;/strong&gt;*, so you get its &lt;code&gt;node_modules&lt;/code&gt; and source files nested in your project, causing the same duplication problem as &lt;code&gt;npm link&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It runs pre/post-install scripts by default&lt;/strong&gt; — the exact mechanism behind a lot of supply-chain security incidents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The command is clunky&lt;/strong&gt; (an absolute local path that differs per machine), so you can't easily script it for a whole team.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The dependency-duplication trap
&lt;/h3&gt;

&lt;p&gt;This one deserves its own spotlight, because it's the subtlest failure. If &lt;code&gt;my-lib&lt;/code&gt; has its own &lt;code&gt;node_modules&lt;/code&gt; (say, after you ran &lt;code&gt;npm install&lt;/code&gt; inside it), linking it gives you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/node_modules/react              ← used by my-app
/path/to/my-lib/node_modules/react     ← used by my-lib (its own copy)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two independent copies of the same library at runtime. For stateless utilities — &lt;code&gt;lodash&lt;/code&gt;, &lt;code&gt;axios&lt;/code&gt;, &lt;code&gt;zod&lt;/code&gt; — harmless. But for anything stateful or using &lt;code&gt;instanceof&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt; – &lt;em&gt;"Invalid hook call"&lt;/em&gt;, because hooks are bound to one specific React instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React Router, Redux&lt;/strong&gt; – context doesn't cross the two instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any singleton&lt;/strong&gt; – two objects where you needed one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the deepest issue: &lt;strong&gt;neither &lt;code&gt;npm link&lt;/code&gt; nor &lt;code&gt;npm install &amp;lt;path&amp;gt;&lt;/code&gt; reproduce the package as it will look once published to npm.&lt;/strong&gt; They wire a direct connection to your source directory — source files and the package's own deps included. Sometimes useful, but if your goal is to &lt;em&gt;eliminate draft releases&lt;/em&gt;, you want to get as close as possible to the published artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually wanted: a tiny local registry
&lt;/h2&gt;

&lt;p&gt;Strip the problem down: the pain is slow draft releases through a &lt;em&gt;remote&lt;/em&gt; registry. The obvious fix is to use a &lt;em&gt;local&lt;/em&gt; one during development. A heavily simplified one — for most work, two commands are enough: publish to the local store, and install from it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzjc9znrwfoz6bifj1yb6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzjc9znrwfoz6bifj1yb6.png" alt="Diagram showing the fast local release flow: kley publish → kley install, no remote registry involvedarticles/v8nyyprq92v5i7wnrl08.png" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This already exists — &lt;a href="https://github.com/wclr/yalc" rel="noopener noreferrer"&gt;&lt;strong&gt;yalc&lt;/strong&gt;&lt;/a&gt;. It's a local repository for your in-progress packages, and it's genuinely good. I used it for years and recommended it to colleagues.&lt;/p&gt;

&lt;p&gt;But two things bother me about it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It's written in TypeScript, so it runs on Node.js.&lt;/strong&gt; I work across a dozen Node projects — some on 18, some on 24 — switching with &lt;code&gt;nvm&lt;/code&gt;. Install yalc under Node 20, publish, switch the host project to Node 18 — and yalc is just &lt;em&gt;gone&lt;/em&gt;. Reinstall it. For every Node version. That's the recurring tax of CLIs built on the runtime they operate on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;yalc link&lt;/code&gt; only works for packages without dependencies.&lt;/strong&gt; It symlinks to the &lt;em&gt;copy&lt;/em&gt; in &lt;code&gt;.yalc/&lt;/code&gt; (a published-shape copy, i.e. without &lt;code&gt;node_modules&lt;/code&gt;), so a library with its own dependencies can't resolve them.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is kley
&lt;/h2&gt;

&lt;p&gt;So: why not make a yalc-style tool that fixes the parts that bug me. A plain binary with no Node.js dependency, and an even simpler, more explicit API (in yalc you have to run &lt;code&gt;npm i&lt;/code&gt; manually after &lt;code&gt;add&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;That's &lt;a href="https://github.com/sumbad/kley" rel="noopener noreferrer"&gt;&lt;strong&gt;kley&lt;/strong&gt;&lt;/a&gt; — a local package manager that doesn't depend on Node.js directly. A tool that glues local packages and projects together.&lt;/p&gt;

&lt;p&gt;It's written in &lt;strong&gt;Rust&lt;/strong&gt;. The ecosystem has everything the job needs out of the box: filesystem work, JSON handling, spawning processes, cross-platform compilation. Binaries for Linux, Windows, and macOS are built with &lt;a href="https://github.com/axodotdev/cargo-dist" rel="noopener noreferrer"&gt;cargo-dist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want a follow-up on the Rust implementation details — say so in the comments and I'll write it up next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux / macOS&lt;/span&gt;
curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://github.com/sumbad/kley/releases/latest/download/kley-installer.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Windows&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Bypass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; irm https://github.com/sumbad/kley/releases/latest/download/kley-installer.ps1 | iex"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; also install via npm (&lt;code&gt;kley-cli&lt;/code&gt;), but I wouldn't recommend it unless you stick to a single Node version — that reintroduces the very problem we're solving.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Using kley
&lt;/h2&gt;

&lt;p&gt;If you've used yalc, the concept will feel familiar. Two common scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1 — stable development: &lt;code&gt;publish → install&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The main, most reliable flow. You're actively developing a library and testing it through a project on the same machine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;strong&gt;library&lt;/strong&gt; dir — &lt;code&gt;kley publish&lt;/code&gt;: copies files to &lt;code&gt;~/.kley/packages/&amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In your &lt;strong&gt;project&lt;/strong&gt; dir — &lt;code&gt;kley install &amp;lt;name&amp;gt;&lt;/code&gt;: copies files to &lt;code&gt;.kley/&lt;/code&gt;, updates &lt;code&gt;kley.lock&lt;/code&gt;, and runs the native package manager to install into &lt;code&gt;node_modules/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Change code in the library, then &lt;code&gt;kley publish --push&lt;/code&gt;: updates every linked project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkd0uy4pgxk9gldzw14ux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkd0uy4pgxk9gldzw14ux.png" alt="Sequence diagram of the kley publish → kley install workflow with publish --push iteration" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ After step 2, just &lt;code&gt;kley publish --push&lt;/code&gt; — no repeated &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;⚠️ This modifies &lt;code&gt;package.json&lt;/code&gt;. Before committing, restore the real npm version, or the dependency list will look "broken" to CI and teammates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Scenario 2 — quick edits: &lt;code&gt;publish → link&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;For fast, temporary edits when you don't want to touch &lt;code&gt;package.json&lt;/code&gt;. It creates a direct symlink from your project's &lt;code&gt;node_modules&lt;/code&gt; to the library's source. Fastest feedback, but — like &lt;code&gt;npm link&lt;/code&gt; — less durable.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;strong&gt;library&lt;/strong&gt; dir — &lt;code&gt;kley publish&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In your &lt;strong&gt;project&lt;/strong&gt; dir — &lt;code&gt;kley link &amp;lt;name&amp;gt;&lt;/code&gt;: symlinks &lt;code&gt;node_modules/&amp;lt;name&amp;gt;&lt;/code&gt; directly to the library source — &lt;strong&gt;no &lt;code&gt;npm install&lt;/code&gt; needed&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Edit the library: the project sees changes &lt;strong&gt;instantly&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yrijpu12cbey7esmfa9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yrijpu12cbey7esmfa9.png" alt="Sequence diagram of the kley publish → kley link workflow, showing direct symlink to library source" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Running &lt;code&gt;npm install&lt;/code&gt; deletes the symlink. Restore it instantly with &lt;code&gt;kley install&lt;/code&gt; (restores everything from &lt;code&gt;kley.lock&lt;/code&gt;) or &lt;code&gt;kley link &amp;lt;name&amp;gt;&lt;/code&gt; again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;link&lt;/code&gt; insight: best of yalc &lt;em&gt;and&lt;/em&gt; &lt;code&gt;npm link&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the part I'm most happy with. There are two ways a tool can wire a link:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;yalc&lt;/strong&gt; links to the &lt;code&gt;.yalc/&lt;/code&gt; copy — published-shape, no &lt;code&gt;node_modules&lt;/code&gt; → breaks for packages with dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;npm link&lt;/code&gt;&lt;/strong&gt; links to the source directory → dependencies resolve, but it has none of yalc's registry, restore, or multi-link conveniences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;kley takes the best of both: from yalc, a local registry with metadata, link restoration, and multiple simultaneous links without conflicts; from &lt;code&gt;npm link&lt;/code&gt;, a symlink pointing at the &lt;strong&gt;library's source directory&lt;/strong&gt; — so &lt;code&gt;kley link&lt;/code&gt; &lt;strong&gt;works for packages with dependencies too&lt;/strong&gt;, resolving them from the library's own &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest trade-off:&lt;/strong&gt; because it links to source (like &lt;code&gt;npm link&lt;/code&gt;), it &lt;em&gt;inherits&lt;/em&gt; the singleton-duplication issue — if the library resolves React from its own &lt;code&gt;node_modules&lt;/code&gt;, you can get two Reacts. kley detects this and &lt;strong&gt;warns you&lt;/strong&gt;, suggesting &lt;code&gt;kley install&lt;/code&gt; instead. And &lt;code&gt;kley link&lt;/code&gt; deliberately does &lt;strong&gt;not&lt;/strong&gt; reproduce the "as-published" environment — you see the library in its dev state (source files, local deps). That's a conscious trade for live-development speed. When you need fidelity to the published artifact, that's &lt;code&gt;kley install&lt;/code&gt;/&lt;code&gt;add&lt;/code&gt;, not &lt;code&gt;link&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few things worth knowing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safe by default.&lt;/strong&gt; &lt;code&gt;kley&lt;/code&gt; runs the package manager with &lt;code&gt;--ignore-scripts&lt;/code&gt;, so &lt;code&gt;preinstall&lt;/code&gt;/&lt;code&gt;install&lt;/code&gt;/&lt;code&gt;postinstall&lt;/code&gt; from the installed package &lt;strong&gt;don't run&lt;/strong&gt;. Less risk of arbitrary code execution on a local install. Native modules that genuinely need scripts: run the PM manually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast paths.&lt;/strong&gt; If a package's &lt;code&gt;dependencies&lt;/code&gt;/&lt;code&gt;peerDependencies&lt;/code&gt; haven't changed since last install, &lt;code&gt;kley&lt;/code&gt; skips the package manager and copies files straight to &lt;code&gt;node_modules/&amp;lt;pkg&amp;gt;&lt;/code&gt;. For dependency-free packages it goes further and symlinks directly. Iterative &lt;code&gt;publish → install&lt;/code&gt; gets noticeably faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-detects the package manager&lt;/strong&gt; (npm / pnpm / yarn) from lockfiles or the &lt;code&gt;packageManager&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproduces published output.&lt;/strong&gt; &lt;code&gt;kley publish&lt;/code&gt; respects &lt;code&gt;files&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;.npmignore&lt;/code&gt;, and &lt;code&gt;.kleyignore&lt;/code&gt; — only files that would end up in the real npm package are copied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kley.lock&lt;/code&gt;&lt;/strong&gt; tracks installed packages, versions, and link type (install/link). Run &lt;code&gt;kley install&lt;/code&gt; with no args to restore everything after &lt;code&gt;git pull&lt;/code&gt; or on a new machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--no-save&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;kley install --no-save &amp;lt;name&amp;gt;&lt;/code&gt; installs into &lt;code&gt;node_modules/&lt;/code&gt; and updates &lt;code&gt;kley.lock&lt;/code&gt; &lt;strong&gt;without touching &lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt;. yalc has no direct equivalent — its "don't touch package.json" is &lt;code&gt;link&lt;/code&gt;, which (as above) doesn't pull dependencies (the feature was requested but never implemented — &lt;a href="https://github.com/wclr/yalc/issues/256" rel="noopener noreferrer"&gt;yalc issue #256&lt;/a&gt;). kley gives you both at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strips &lt;code&gt;devDependencies&lt;/code&gt;&lt;/strong&gt; when copying a package into your project, keeping &lt;code&gt;node_modules&lt;/code&gt; lean. The original in the registry is untouched. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Version control
&lt;/h3&gt;

&lt;p&gt;Treat &lt;code&gt;.kley/&lt;/code&gt; and &lt;code&gt;kley.lock&lt;/code&gt; following two-mode approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For temporary local development, add both to &lt;code&gt;.gitignore&lt;/code&gt; and use &lt;code&gt;kley link&lt;/code&gt; or &lt;code&gt;kley install --no-save&lt;/code&gt; to avoid touching &lt;code&gt;package.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For shared WIP packages that are part of the project's codebase, keep them under version control to ensure consistency across the team, swapping to remote-registry versions when the package stabilises.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  More commands
&lt;/h3&gt;

&lt;p&gt;Beyond &lt;code&gt;publish&lt;/code&gt;, &lt;code&gt;install&lt;/code&gt;, and &lt;code&gt;link&lt;/code&gt;, kley provides &lt;code&gt;kley add&lt;/code&gt; (copy to &lt;code&gt;.kley/&lt;/code&gt; and update &lt;code&gt;package.json&lt;/code&gt; without running the PM), &lt;code&gt;kley update&lt;/code&gt; (refresh from registry), &lt;code&gt;kley remove&lt;/code&gt; (unlink a dependency), and &lt;code&gt;kley unpublish&lt;/code&gt; (remove from the central store). See the &lt;a href="https://github.com/sumbad/kley" rel="noopener noreferrer"&gt;README&lt;/a&gt; for the full reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Benchmarked on a realistic fixture: a library with &lt;code&gt;files: ["dist"]&lt;/code&gt;, &lt;code&gt;devDependencies&lt;/code&gt;, and compiled output; a host app with &lt;code&gt;package-lock.json&lt;/code&gt;. For yalc tools, cold start includes &lt;code&gt;yalc add&lt;/code&gt; + &lt;code&gt;npm install&lt;/code&gt; — to reach the same final &lt;code&gt;node_modules&lt;/code&gt; state as &lt;code&gt;kley install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To be honest, there are now some forks of &lt;code&gt;yalc&lt;/code&gt;, we will use &lt;a href="https://www.npmjs.com/package/@jimsheen/yalc" rel="noopener noreferrer"&gt;&lt;code&gt;@jimsheen/yalc&lt;/code&gt;&lt;/a&gt; for comparison. It's a drop-in replacement with better performance and proper pnpm/workspaces support.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;&lt;code&gt;kley&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;yalc&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;@jimsheen/yalc&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cold start (publish → install)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~9 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~390 ms&lt;/td&gt;
&lt;td&gt;~437 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iteration (publish --push)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~106 ms&lt;/td&gt;
&lt;td&gt;~125 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On a MacBook M4 Pro, kley is &lt;strong&gt;~45× faster than yalc&lt;/strong&gt; on the cold start and &lt;strong&gt;~16× faster&lt;/strong&gt; on iteration. The gap comes from startup cost: every &lt;code&gt;yalc&lt;/code&gt; invocation spins up the Node.js runtime; &lt;code&gt;kley&lt;/code&gt; is a native binary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;A subjective overview — its point is to show kley's motivation, not to crown a winner.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;npm link&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;yalc&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;@jimsheen/yalc&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;kley&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Project &amp;amp; library on different Node.js versions&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reproduces the published package&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works for libraries with dependencies&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple local dependencies at once&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monorepo / workspaces&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The takeaways: if you live on Node and need workspaces, the maintained fork is a fine choice. kley's edge is being a &lt;strong&gt;single self-contained binary independent of Node.js&lt;/strong&gt;, with &lt;strong&gt;safe-by-default installs&lt;/strong&gt; and a &lt;code&gt;link&lt;/code&gt; that handles dependencies.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kley&lt;/code&gt; is early. Today it covers the core workflow well, but the roadmap is real work, not a victory lap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Workspaces / monorepo support&lt;/strong&gt; is partial — if that's central to you, the fork above is currently stronger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No watch mode yet&lt;/strong&gt; — updates are semi-manual via &lt;code&gt;publish --push&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Planned: deeper pnpm/yarn/workspace support, watch mode, pre/post hooks, registry-validation commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you've ever fought the draft-release loop or &lt;code&gt;npm link&lt;/code&gt;, give &lt;a href="https://github.com/sumbad/kley" rel="noopener noreferrer"&gt;kley&lt;/a&gt; a shot and tell me how it goes. Questions and ideas welcome in &lt;a href="https://github.com/sumbad/kley/issues" rel="noopener noreferrer"&gt;GitHub issues&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://github.com/sumbad/kley" rel="noopener noreferrer"&gt;kley&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://github.com/wclr/yalc" rel="noopener noreferrer"&gt;yalc&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/@jimsheen/yalc" rel="noopener noreferrer"&gt;@jimsheen/yalc fork&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://hirok.io/posts/avoid-npm-link" rel="noopener noreferrer"&gt;4 reasons to avoid &lt;code&gt;npm link&lt;/code&gt;&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://docs.npmjs.com/cli/v8/commands/npm-link" rel="noopener noreferrer"&gt;npm link docs&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://github.com/yarnpkg/yarn/issues/1761#issuecomment-259706202" rel="noopener noreferrer"&gt;yarn workspace issue with npm link&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://github.com/axodotdev/cargo-dist" rel="noopener noreferrer"&gt;cargo-dist&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>npm</category>
      <category>javascript</category>
      <category>rust</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
