Home 📖 Documentation Hub Ecosystems TUI Monitor CLI Command Reference
⚡ linkd v0.2.2 Official Documentation

Overview & Introduction

linkd is a local-dev link daemon engineered to solve the chronic fragility of package linking in modern monorepos and multi-repository workspaces.

Traditional workflows like npm link, yarn link, composer link, pip install -e, or "file:../lib" dependencies suffer from a fundamental problem: package managers assume they own the target directory. As soon as you run npm install, composer update, or uv sync, your local symlinks are obliterated and replaced with registry packages.

linkd introduces Kubernetes-style continuous reconciliation to local paths. A lightweight background daemon maintains a registry of desired link states, watches lockfiles for reinstall events, and instantly restores your dev packages within milliseconds without touching manifest files.

💡 Why Zero Manifest Edits Matter
Relative path dependencies like "my-pkg": "file:../packages/my-pkg" frequently leak into package.json, composer.json, or pyproject.toml and get accidentally committed to Git. With linkd, your manifests stay 100% untouched and clean.

📦 1-Line Installation

linkd ships as a standalone, zero-dependency compiled binary for Linux, macOS, and Windows.

Linux & macOS (curl / bash)

Terminal
curl -fsSL https://raw.githubusercontent.com/pinoox/linkd/master/install.sh | bash

Windows (PowerShell)

PowerShell
irm https://raw.githubusercontent.com/pinoox/linkd/master/install.ps1 | iex

Via Cargo (from source)

Cargo
cargo install --path crates/linkd-cli --locked

⚡ Quickstart Tutorial

linkd offers two powerful workflows designed for modern monorepos and local package development:

Workflow 1: Global Package Workflow (2-Step, Recommended)

Register a package once globally, then link it into any project with a single command:

Terminal (Inside Package Folder)
$ cd packages/my-ui-kit
$ linkd register
✓ Registered reusable package: @acme/ui-kit
  Usage: run `linkd use @acme/ui-kit` in any consumer project directory.
Terminal (Inside Consumer Apps)
$ cd apps/web-app
$ linkd use @acme/ui-kit
✓ Linked @acme/ui-kit → ./apps/web-app
  sync target: ./apps/web-app/node_modules/@acme/ui-kit

$ cd ../mobile-app && linkd use @acme/ui-kit
✓ Linked @acme/ui-kit → ./apps/mobile-app

Workflow 2: Direct Path Linking

Point directly to your library source and consumer app:

Command
$ linkd link ./packages/my-ui-kit ./apps/web-app
✓ Linked @acme/ui-kit → ./apps/web-app

🔄 Multi-Consumer Real-Time Syncing

When one package is linked into multiple apps (e.g. Web, Mobile, Desktop), changing a single file in the package automatically synchronizes all consumer apps in real time in parallel!


🔄 Architecture: The Reconciliation Loop

linkd operates similarly to a Kubernetes controller:

  1. Desired State: Stored globally in ~/.linkd/registry.json.
  2. Actual State: Monitored on the filesystem via notify file watchers and .linkd-marker.json files.
  3. Reconciliation Loop: When source files change, changes are debounced and synced. When lockfiles change (indicating an install occurred), the target directory is atomically restored.
Reconciler Architecture Diagram
┌────────────────────────────────────────────────────────────────────────┐
│                        ~/.linkd/registry.json                          │
│                          (Global State Store)                          │
└───────────────────────────────────┬────────────────────────────────────┘
                                    │
                         ┌──────────▼──────────┐
                         │    linkd daemon     │
                         │ (Background Engine) │
                         └──────────┬──────────┘
                                    │
       ┌────────────────────────────┼────────────────────────────┐
       │ (File Watchers)            │ (Reconciliation Engine)    │ (IPC Server)
       ▼                            ▼                            ▼
  Source Changes             Reinstall Event               Live Dashboard
  - Debounced sync           - Lockfile markers            - Subscriptions
  - Cache filter             - Atomic directory swap       - Pause/Resume
       │                            │
       └────────────────────────────┴────────────────────────────┐
                                                                 ▼
                                                  Target Project (.linkd-marker)
                                                  - node_modules/<pkg>
                                                  - vendor/<pkg>
                                                  - .venv/Lib/site-packages/<pkg>

🛡️ pnpm Global Store Safety Gate

In pnpm, packages in node_modules are hardlinks to a global content-addressable store (~/.pnpm-store). Writing directly into node_modules would contaminate the global cache for all projects on your computer.

linkd automatically detects pnpm, isolates the link by copying into a project-local shadow directory:
./apps/web/node_modules/.linkd-shadow/<package>
and repoints the package symlink safely to that shadow copy.

⚡ Atomic Swaps & Marker Provenance

To prevent build tools (webpack, vite, tsc, cargo) from crashing during writes, linkd prepares files in a temporary sibling directory and atomically swaps it using filesystem renames.

🔁 Anti-Watch-Loop Guards

When custom paths or monorepos are linked, source and target folders could inadvertently nest. linkd strictly validates that neither path is a prefix or ancestor of the other, preventing infinite watcher loops.


🟢 JavaScript & TypeScript (npm, pnpm, yarn, bun)

Automatically resolves package.json names, scoped packages (e.g. @org/pkg), and targets node_modules/<name>.

Example
linkd link ./packages/ui-kit ./apps/nextjs-app

💙 Flutter & Dart (pubspec.yaml)

Detects pubspec.yaml, extracts the package name, and synchronizes into consumer/.dart_tool/packages/<package_name> with instant hot reload compatibility.

Example
linkd link ./packages/flutter_ui_kit ./apps/flutter_mobile_app

🔷 .NET (C# / F# / NuGet)

Parses *.csproj and Directory.Build.props for <PackageId> and syncs compiled libraries into consumer/packages/<package_id> without requiring temporary <ProjectReference> edits.

Example
linkd link ./src/Acme.Logging ./src/Acme.ApiServer

💎 Ruby (Gems & Bundler)

Parses *.gemspec definitions and syncs gem files into consumer/vendor/bundle/gems/<gem_name>, making local gem development painless in Rails applications.

Example
linkd link ./gems/acme_auth ./apps/rails_app

🐦 Swift (Swift Package Manager)

Detects Package.swift manifests and syncs into consumer SPM checkouts at consumer/.build/checkouts/<package_name> for both command-line builds and Xcode.

Example
linkd link ./packages/SwiftUiKit ./apps/SwiftApp

💧 Elixir (Mix & Phoenix)

Parses app: :name from mix.exs and syncs directly into consumer/deps/<app_name> for Phoenix and OTP projects without modifying mix.exs.

Example
linkd link ./plugins/elixir_auth ./apps/phoenix_web

🐘 PHP (Composer)

Detects composer.json, extracts the vendor/package namespace, and syncs into consumer/vendor/<vendor>/<package>.

Example
linkd link ./packages/acme-logger ./apps/laravel-api
Autoload Hint
If you add new PHP classes, linkd alerts you if composer dump-autoload is needed to regenerate classmaps.

🐍 Python (uv / pip / poetry / flit)

Detects PEP 621 pyproject.toml, setup.py, or setup.cfg. Automatically locates virtual environments (.venv/Lib/site-packages on Windows, .venv/lib/python*/site-packages on Unix).

Example
linkd link ./packages/ml-core ./apps/fastapi-service

🐹 Go (Go Modules & Vendor)

Extracts the module path from go.mod and syncs directly into consumer/vendor/<module_path>.

Example
linkd link ./packages/auth-lib ./apps/go-microservice

🦀 Rust (Cargo Crates)

Parses [package.name] from Cargo.toml and vendors into consumer/vendor/<crate_name>.

Example
linkd link ./crates/common-types ./apps/rust-server

☕ Java & Kotlin (JVM - Maven / Gradle)

Parses Group and Artifact IDs from pom.xml, build.gradle, or build.gradle.kts and routes to consumer/libs/<artifact>.

Example
linkd link ./packages/java-sdk ./apps/spring-app

📁 Custom Paths (Any Framework / Asset Folder)

Use the --target flag to link any directory to any destination without package manager assumptions:

Example
linkd link ./shared-assets ./apps/electron-app --target ./apps/electron-app/src/assets/shared

🖥️ Live TUI Monitor Dashboard

Launch the dashboard with linkd monitor (or aliases linkd top / linkd dashboard):

The monitor displays links in a hierarchical tree view: each package appears as a header with all its consumer projects listed underneath as collapsible branches.

Example: Hierarchical Tree View
  ▼ 📦 @acme/ui-kit  [npm]  (3 consumers)
  │  Source: ~/projects/monorepo/packages/ui-kit
  ├── 📂 apps/web-app     → node_modules/@acme/ui-kit  [✓ synced]
  ├── 📂 apps/mobile-app  → node_modules/@acme/ui-kit  [✓ synced]
  └── 📂 apps/admin       → node_modules/@acme/ui-kit  [⏸ paused]

  ▼ 📦 my_flutter_widget  [dart/flutter]  (1 consumer)
  └── 📂 apps/flutter_app → .dart_tool/packages/my_flutter_widget  [✓ synced]

🧙 Interactive Setup Wizard (linkd init)

If you don't want to memorize command-line flags and path arguments, linkd init provides an interactive terminal guide that asks questions step-by-step to link any package:

Terminal
$ linkd init

? Link type:
  > npm package (JavaScript / TypeScript)
    flutter / dart package (pubspec.yaml)
    .NET package (C# / F# / NuGet)
    ruby gem (Bundler)
    swift package (SPM)
    elixir package (Mix)
    composer package (PHP)
    python (uv/pip/poetry)
    go module
    rust (cargo crate)
    java/kotlin (jvm)
    custom path

? Source package directory: ./packages/my-ui-kit
? Consumer project directory: ./apps/web-app
✓ Suggested sync strategy: reflink / copy
? Start background daemon after linking? Yes
? Create link now? Yes

✓ Link '@acme/my-ui-kit' successfully registered and synced!

Step-by-Step Flow:


⚙️ Daemon Lifecycle & Service Setup

Manage the background daemon directly via CLI:

Commands
linkd start        # Start detached background daemon
linkd stop         # Graceful shutdown via IPC
linkd stop --force # Terminate PID immediately
linkd watch        # Run in foreground with live terminal logs
linkd status       # Instant snapshot of PID & link count
linkd logs -f      # Tail structured daemon logs

🩺 Running Diagnostics (linkd doctor)

Use linkd doctor to inspect environment health, socket permissions, inotify limits, and package manager availability:

Command
linkd doctor
linkd doctor --explain pnpm-store
linkd doctor --explain autostart
linkd doctor --explain python

🐧 Systemd & 🍎 macOS Launchd Auto-Start

To run linkd automatically on system login:

macOS Launchd plist (~/Library/LaunchAgents/dev.linkd.daemon.plist):

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>dev.linkd.daemon</string>
  <key>ProgramArguments</key>
  <array><string>/usr/local/bin/linkd</string><string>--daemon-internal</string></array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict>
</plist>

Linux Systemd User Unit (~/.config/systemd/user/linkd.service):

INI
[Unit]
Description=linkd local-dev link daemon
After=default.target

[Service]
ExecStart=/usr/local/bin/linkd --daemon-internal
Restart=always

[Install]
WantedBy=default.target

📖 CLI Command Reference

Search and explore all commands, options, and operational subcommands in linkd v0.2.2:

Command Flags & Arguments Description
linkd register [path] [--name <name>] [--ecosystem <type>] (aliases: pin, add) Register current package globally for reuse across multiple projects.
linkd use <package> [consumer] [--target <path>] (aliases: on, attach) Link a globally registered package into the current/specified project.
linkd packages none (alias: pinned) List all globally registered reusable packages.
linkd unregister <package> (alias: unpin) Remove package from global registry.
linkd link <source> [consumer] [--target <path>] [--ecosystem <type>] [--copy|--hardlink|--link] [--no-daemon] Directly register and sync local package into consumer.
linkd unlink <target|name|source> Remove an active link and clean marker files.
linkd list [--json] [--details] (aliases: ls, status) List all active links with sync status and health indicators.
linkd pause <target|name|all> Temporarily pause reconciliation for target link(s).
linkd resume <target|name|all> Resume active reconciliation and run immediate sync.
linkd sync [target|name|all] [--force] (alias: reconcile) Manually trigger an immediate reconciliation run.
linkd monitor [--interval <ms>] (aliases: top, dashboard) Launch interactive full-screen TUI terminal monitor.
linkd init none (alias: wizard) Interactive setup wizard for guided linking.
linkd start [--port <p>] [--foreground] Start the background daemon process.
linkd stop [--force] Stop running linkd daemon.
linkd doctor [--explain <topic>] Run environment & permission diagnostic checks.
linkd logs [-f|--follow] Tail background daemon logs.
linkd version [-v, --json] Print version, target OS, architecture, and supported ecosystems.
linkd completions <shell> Generate shell completions (bash, zsh, fish, pwsh).

❓ FAQ & Troubleshooting

Does linkd modify my package.json or composer.json?

No. linkd never modifies your source manifests. This ensures your Git working tree remains completely clean and no local file paths are accidentally committed to version control.

How does linkd detect reinstalls?

Rather than monitoring process trees which is fragile across operating systems, linkd sets up inotify/FSEvents/ReadDirectoryChanges watchers on package manager completion lockfiles (e.g. package-lock.json, pnpm-lock.yaml, composer.lock, uv.lock, go.sum, Cargo.lock). When an install completes, linkd immediately re-evaluates the target directory.

Can I sync between arbitrary folders?

Yes. Use linkd link <source> <consumer> --target <path> to sync any folder into any destination with automatic loop-guarding.

Ready to streamline your local development?

Install linkd v0.2.2 today and enjoy rock-solid local package synchronization.

Get Started with linkd