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

make-derive

v0.1.1

Published

Content-addressed LLM generation CLI — make, but the compiler is an LLM

Readme

derive

Hyper-configurable make where the compiler is an LLM.

A content-addressed, declarative, LLM-powered build system for derived files.

derive              # run tasks with changes
derive readme       # run specific task
derive --status     # see what's stale
derive --force      # ignore hashes, run anyway

What It Does

  1. Hash your source files (SHA-256)
  2. Compare against .derive.lock
  3. If changed: assemble prompt -> run your LLM CLI -> update lock
  4. If unchanged: skip

No more regenerating docs when nothing changed. No more forgetting to update generated files when sources change.

Quick Start

# Install
npx make-derive --init

# Edit derive.jsonc to define your tasks
# Then run
npx make-derive

Or install globally:

npm i -g make-derive
derive

Installation

Global install

npm i -g make-derive    # npm
bun add -g make-derive  # bun
pnpm add -g make-derive # pnpm

Run without installing

npx make-derive         # npm
bunx make-derive        # bun
pnpx make-derive        # pnpm

From source

git clone https://github.com/CyrusNuevoDia/derive
cd derive
bun install
bun run build:bin  # Creates bin/derive

CLI Reference

derive                     Run all tasks with changes
derive <task>              Run specific task if changed
derive --force [task]      Run regardless of hash state
derive --dry-run [task]    Show what would run
derive --status            Show per-task change status
derive --init              Create starter derive.jsonc
derive --config <path>     Use specific config file
derive --help              Print help
derive --version           Print version

Flags

| Flag | Short | Description | |------|-------|-------------| | --help | -h | Print help message and exit | | --version | -v | Print version number and exit | | --force | -f | Run task(s) regardless of hash state | | --dry-run | -n | Show what would run without executing | | --status | -s | Show per-task change status | | --init | | Create a starter derive.jsonc in the current directory | | --config <path> | -c | Use a specific config file instead of auto-discovery |

Examples

# Run all tasks that have changes
derive

# Run only the "readme" task (if changed)
derive readme

# Force run the "readme" task even if nothing changed
derive --force readme
derive -f readme

# See what would run without actually running
derive --dry-run
derive -n

# Check which tasks have pending changes
derive --status
derive -s

# Use a specific config file
derive --config ./config/derive.jsonc
derive -c ./config/derive.jsonc

# Initialize a new project
derive --init

Configuration

Derive looks for config files in this order:

  1. derive.ts (TypeScript, for dynamic configs)
  2. derive.jsonc (JSON with comments)
  3. derive.json (plain JSON)
  4. derive.toml (TOML)

Config Structure

| Field | Type | Required | Description | |-------|------|----------|-------------| | runner | string | Yes | Default command to execute. Must contain {prompt} placeholder. | | tasks | object | Yes | Map of task names to task configurations |

Task Structure

| Field | Type | Required | Description | |-------|------|----------|-------------| | prompt | string | Yes | The prompt/instruction for the LLM | | sources | string[] | Yes | Glob patterns for source files to watch | | exclude | string[] | No | Glob patterns for files to exclude | | runner | string | No | Override the default runner for this task |

JSONC Example

{
  // Default runner command - {prompt} will be replaced with the assembled prompt
  "runner": "claude --allowed-tools Read,Write,Edit --print {prompt}",
  "tasks": {
    "readme": {
      "prompt": "Update README.md based on the current codebase. Keep it concise.",
      "sources": ["src/**/*.ts"],
      "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
    },
    "api-docs": {
      "prompt": "Generate API documentation for the public exports.",
      "sources": ["src/index.ts", "src/types.ts"],
      // Override runner for this specific task
      "runner": "llm -m gpt-4o {prompt}"
    },
    "changelog": {
      "prompt": "Update CHANGELOG.md with recent changes.",
      "sources": ["src/**/*.ts", "package.json"]
    }
  }
}

JSON Example

{
  "runner": "claude --print {prompt}",
  "tasks": {
    "readme": {
      "prompt": "Update README.md based on the codebase.",
      "sources": ["src/**/*.ts"],
      "exclude": ["src/**/*.test.ts"]
    }
  }
}

TOML Example

runner = "claude --print {prompt}"

[tasks.readme]
prompt = "Update README.md based on the codebase."
sources = ["src/**/*.ts"]
exclude = ["src/**/*.test.ts"]

[tasks.api-docs]
prompt = "Generate API documentation."
sources = ["src/index.ts", "src/types.ts"]
runner = "llm -m gpt-4o {prompt}"

TypeScript Example

Use derive.ts for dynamic configuration:

// derive.ts
import type { DeriveConfig } from "make-derive";

export default async (): Promise<DeriveConfig> => {
  const pkg = await Bun.file("package.json").json();

  return {
    runner: `claude --allowed-tools Read,Write,Edit --print {prompt}`,
    tasks: {
      readme: {
        prompt: `Update README.md for ${pkg.name}@${pkg.version}.
                 Include installation instructions and API reference.`,
        sources: ["src/**/*.ts"],
        exclude: ["src/**/*.test.ts"],
      },
      changelog: {
        prompt: `Update CHANGELOG.md. Current version is ${pkg.version}.`,
        sources: ["src/**/*.ts", "package.json"],
      },
    },
  };
};

You can also export a static object:

// derive.ts
export default {
  runner: "claude --print {prompt}",
  tasks: {
    readme: {
      prompt: "Update the README.",
      sources: ["src/**/*.ts"],
    },
  },
};

How It Works

Content Addressing

Derive uses SHA-256 hashes to track file changes:

  • Per-file hashes: Each source file is hashed individually using streaming to handle large files
  • Merkle root: A combined hash of all files for fast "anything changed?" checks
  • Incremental updates: Only changed files trigger regeneration

Prompt Assembly

When a task runs, derive assembles a prompt in XML format:

<prompt>Your task prompt here</prompt>
<changed-files>src/foo.ts, src/bar.ts</changed-files>

This tells your LLM what to do and which files changed, so it can focus on relevant updates.

Shell Execution

Runners are executed in a login shell (-l -i -c) to ensure your PATH and environment are properly loaded from your shell config (.zshrc, .bashrc, etc.).

The {prompt} placeholder is safely shell-escaped before substitution.

The Lockfile

.derive.lock tracks the state of your last run:

{
  "version": 1,
  "tasks": {
    "readme": {
      "last_run": "2024-01-15T10:30:00.000Z",
      "sources_hash": "sha256:abc123...",
      "files": {
        "src/index.ts": "sha256:def456...",
        "src/config.ts": "sha256:789abc..."
      }
    }
  }
}

Commit this file. It enables:

  • Incremental builds in CI
  • Team-wide consistency
  • Audit trail of when files were last regenerated

Runner Examples

Derive is runner-agnostic. Use any CLI that accepts a prompt:

{
  // Claude Code
  "runner": "claude --allowed-tools Read,Write,Edit --print {prompt}",

  // OpenAI via llm CLI
  "runner": "llm -m gpt-4o {prompt}",

  // Anthropic via llm CLI
  "runner": "llm -m claude-3-opus {prompt}",

  // Local models via Ollama
  "runner": "ollama run llama3 {prompt}",

  // Custom script
  "runner": "./scripts/generate.sh {prompt}"
}

Task-Level Runners

Override the default runner per task:

{
  "runner": "claude --print {prompt}",
  "tasks": {
    "readme": {
      "prompt": "Update README.md",
      "sources": ["src/**/*.ts"]
      // Uses default runner: claude
    },
    "quick-task": {
      "prompt": "Fix typos in comments",
      "sources": ["src/**/*.ts"],
      "runner": "llm -m gpt-4o-mini {prompt}"  // Faster, cheaper model
    }
  }
}

Glob Patterns

Source patterns follow standard glob syntax:

| Pattern | Matches | |---------|---------| | src/**/*.ts | All .ts files in src/ recursively | | *.md | All .md files in root | | src/*.ts | .ts files directly in src/ (not nested) | | {src,lib}/**/*.ts | .ts files in both src/ and lib/ |

Exclude patterns remove files from the matched set:

{
  "sources": ["src/**/*.ts"],
  "exclude": [
    "src/**/*.test.ts",
    "src/**/*.spec.ts",
    "src/**/__tests__/**"
  ]
}

Exit Codes

| Code | Meaning | |------|---------| | 0 | Success (all tasks completed or skipped) | | 1 | Task execution failed or invalid arguments | | 2 | No config file found |

License

GPLv3