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

make-derive

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

make-derive

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

latest
Source
npmnpm
Version
0.1.1
Version published
Maintainers
1
Created
Source

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

  • Hash your source files (SHA-256)
  • Compare against .derive.lock
  • If changed: assemble prompt -> run your LLM CLI -> update lock
  • 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

FlagShortDescription
--help-hPrint help message and exit
--version-vPrint version number and exit
--force-fRun task(s) regardless of hash state
--dry-run-nShow what would run without executing
--status-sShow per-task change status
--initCreate a starter derive.jsonc in the current directory
--config <path>-cUse 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:

  • derive.ts (TypeScript, for dynamic configs)
  • derive.jsonc (JSON with comments)
  • derive.json (plain JSON)
  • derive.toml (TOML)

Config Structure

FieldTypeRequiredDescription
runnerstringYesDefault command to execute. Must contain {prompt} placeholder.
tasksobjectYesMap of task names to task configurations

Task Structure

FieldTypeRequiredDescription
promptstringYesThe prompt/instruction for the LLM
sourcesstring[]YesGlob patterns for source files to watch
excludestring[]NoGlob patterns for files to exclude
runnerstringNoOverride 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:

PatternMatches
src/**/*.tsAll .ts files in src/ recursively
*.mdAll .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

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

License

GPLv3

Keywords

llm

FAQs

Package last updated on 24 Feb 2026

Related posts