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

@kitschpatrol/renami

v0.3.0

Published

A CLI tool and TypeScript library for config-driven and content-aware automatic filename management.

Readme

renami

NPM Package @kitschpatrol/renami License: MIT CI

A CLI tool and TypeScript library for config-driven and content-aware automatic filename management.

[!IMPORTANT]

Renami is in early development and specifically targets the author's niche use-cases. It will remain zero-versioned until it's reasonably safe and intuitive to use. Caveat emptor.

Overview

Your files already know their names.

Renami generates file names as a function of file content. It provides a configuration-driven and deterministic approach to automated filename management. Specify how you want certain folders of files to be named in the root of your project, and then run renami to keep all the filenames consistent and up-to-date.

The tool makes it easy to pull specific metadata from a file's content and pass it into simple string-based templates to rename files.

Think of it as a linter + fixer for file names.

I use it to maintain consistent, content-driven filenames for a large collection of Markdown notes.

Renami provides an application-agnostic foundation for the Renami Obsidian plugin.

Despite being configuration-driven, Renami aspires to use convention over configuration wherever possible. Basic filename hygiene like invalid character removal, Unicode normalization, deduplication, truncation, and (optionally) case transformation are all handled automatically and implicitly.

Features

  • Automatically set file names based on file content on a per-directory basis
    • Set Markdown file names from frontmatter fields via simple string-based templates
    • Set Markdown file name from content with CSS-like query selectors
    • Set file names from file metadata like creation dates
  • Easily extensible / customizable
    • Simple cases can be managed with simple template strings
    • Complex cases are possible by implementing custom handlers right in the TypeScript configuration file
    • Built-in support for additional file types is planned but not promised
  • File extension normalization (.jpeg.jpg, etc.)
  • Unicode normalization
  • Max-length name truncation with configurable ... and support for breaking on words
  • Automatic name collision resolution with numeric increments
  • All kinds of text case transformations
  • Support for formatting numbers and dates
  • Idempotent — stable, deterministic file name output across repeat invocations

Non-Features

  • Directory renaming
  • File relocation

Getting started

Dependencies

The renami CLI tool requires Node 20+. The exported APIs are isomorphic, and should work in any relatively recent runtime environment — though in the browser you will need to implement your own file handling and glob matching logic to suit your environment.

Renami is implemented in TypeScript and bundles a complete set of type definitions.

Installation

Install locally to access the CLI commands in a single project or to import the provided APIs:

npm install @kitschpatrol/renami

Or, install globally for access across your system:

npm install --global renami

See the sections below for details on configuration and available commands.

Configuration

Renami depends on configuration to describe how it should rename files when it's run. Internally, it uses cosmiconfig to search for and find relevant configuration in the usual locations.

The library also exports a typed configuration factory function to provide type hinting when authoring configurations.

A trivial configuration might look like this:

// File: "renami.config.ts"
import { defineRenamiConfig } from '@kitschpatrol/renami'

// Typed config factory...
export default defineRenamiConfig({
  rules: [
    {
      // Make all Markdown files kebab case.
      options: {
        caseType: 'kebab',
      },
      pattern: './**/*.md',
    },
  ],
})

A more complex configuration file might look like this:

// File: "renami.config.ts"
import { defineRenamiConfig, transformHelper } from '@kitschpatrol/renami'

// Renami provides factory function helpers for common transform tasks.
const { fileCallback } = transformHelper

export default defineRenamiConfig({
  // Global options become the default for all "rules" below, but may be
  // overridden on a per-rule basis.
  options: {
    dryRun: true,
    maxLength: 50,
  },
  // Each rule targets a group of files with a glob pattern, and specifies how
  // matching filenames should be managed. If a file matches multiple rules, only
  // the LAST rule in the array is applied to the file.
  rules: [
    {
      // Patterns are relative to this config file's location
      pattern: './test/assets/test-basic/**/*',
      // Transform functions take info about a file and return a filename
      // `string`, or `undefined` if no valid transform is possible. Multiple
      // functions can be passed to `transform`, and are evaluated left to
      // right, with the output of one transform passed to the next, unless it
      // returns `undefined`, in which case it's skipped. Additional changes
      // might be made to the filename afterwards depending on 'options'.
      // This one sets the filename to ctime:
      transform: fileCallback(({ fileInfo }) => `I was born at ${fileInfo.ctimeMs}`),
    },
    {
      options: {
        caseType: 'kebab',
      },
      pattern: './test/assets/test-frontmatter/**/*',
      // Transforms can also be simple strings, which are passed into a
      // universal template transformer which provides different template
      // variables for different file types.
      //
      // This one sets filename from the `title` frontmatter field in a Markdown
      // file.
      transform: 'Note-{title}',
    },
    {
      options: {
        caseType: 'kebab',
        // The final file name will be truncated to 15 characters, including the
        // extension
        maxLength: 15,
        truncateOnWordBoundary: false,
      },
      pattern: './test/assets/test-increment/**/*',
      // Example of a simple custom transform function, which takes a context
      // object with info about the file and can do whatever it wants to return
      // a file name string. This function must always be async even if it
      // doesn't actually await anything.
      transform: async (context) =>
        `My file extension is ${context.filePath.ext} and wow what a long name this is!`,
    },
  ],
})

Once you have a configuration file, run renami from the command line or import and invoke the renami() function in the API to automatically discover and execute the renaming rules.

Configuration Options

These may be applied globally at the top level of the configuration file, or on a per-rule basis.

caseType

Enforce a specific letter casing on the final filenames.

collapseDuplicateWhitespace

Replace duplicate whitespace with a single space.

collapseSurplusDelimiters

If a template is missing values and has sections like bla - - bla - , this will collapse extra delimiter strings to yield bla - bla.

defaultName

In rare cases where a path contains only unsafe characters, or when no transformations work in strict mode, this default name is used.

delimiter

The string used to join array values in templates and to collapse surplus delimiters in templates.

dryRun

When true, files aren't actually renamed; the operation is simulated only.

ignoreFolderNotes

Ignore notes matching the containing folder name, as may be the case when using the obsidian-folder-notes plugin.

maxLength

Maximum number of characters in the file name, including file extension but excluding base path. Any automatic truncation strings or increments will count towards this maximum.

strict

If no user-provided transformations work (they all return undefined), then use the default name. Otherwise, the original name is preserved. Technically breaks idempotence.

trim

Trim leading and trailing white space from the file name.

truncateOnWordBoundary

Try to truncate the file name on a word boundary, which might result in file names shorter than the maxLength target.

truncationString

String (like '...') to use when truncation is needed.

validateInput

Run checks to make sure the input file list is valid.

validateOutput

Make sure we're not overwriting a file that wasn't included in the input files.

Templates

Renami implements a simple string-based filename templating system to cover most simple renaming scenarios. (And more complex behavior can always be implemented in JavaScript or TypeScript through a bespoke transform function.)

Generally, {single brackets} denote metadata placeholders related to the file, while {{double brackets}} denote content placeholders from within the file, which might change subtly depending on file types.

Either template style may contain | characters to delimit basic per-placeholder inline transformation commands. More details below.

If a key in a template string placeholder cannot be resolved, it will be replaced with an empty string.

Supported file types

Renami provides convenient template string behavior based on detected file types.

Currently, only Markdown (*.md) support is implemented for string templates. For now, other file types may be renamed by defining a transform function in your Renami configuration.

Markdown

Single braces, { and }, surround accessors to the file's frontmatter object about the file, e.g. File - {date.created} or Meeting about {tags[0]}.

Double braces, {{ and }}, surround selector queries to an AST associated with the file, e.g. {{heading}}.

If no object or selection path can be resolved, then an empty string '' is returned.

Inline formatting

Within either single or double brace template keywords, an optional | character may be followed with a string to perform keyword-specific formatting. The interpolator will make a best-effort attempt to process the resolved value based on the string provided after the |. Multiple formatters may be chained.

Given a string template like 'File {key|format}', the format string will be tested for a match against the following rules, in order:

Case changes

If the format string is a case type name, the content of the template key will be transformed accordingly. This happens before global "options" level case directives, and may be overwritten by global case transformation options.

'Note - {title|uppercase}'Note - TITLE FROM FRONTMATTER.md

Supported strings are 'camel', 'kebab', 'lowercase', 'pascal', 'preserve', 'screaming-kebab', 'screaming-snake', 'sentence', 'slug', 'snake', 'title', 'uppercase'.

Number formatting

Next, renami will attempt to parse the resolved value as a number and format it according to the numerable library's format syntax. (Similar to formats specified by TR35 / ICU 67.)

I have {count|0,0.00}TK.md

Date formatting

Next, it will attempt to parse the resolved value as a date and format it, using patterns based on Unicode Technical Standard #35. (See here for a nice reference.)

'My Note about {{heading}} - {date|yyyy-MM-dd}'My Note about Stuff - 2025-03-15.md

Truncation

Passing a positive integer will trim the value:

'Note - {title|2}'Note - Ti.md

If none of the above value / format string combinations are valid, then the format string is ignored and the resolved value is returned as-is.

Usage

Library

import { renami } from '@kitschpatrol/renami'

// Rename files based on locally discoverable
// configuration, e.g. `renami.config.ts`
const report = await renami()

console.log(report)

// Or specify configuration inline...
const anotherReport = await renami({
  config: {
    rules: [
      {
        // Make all Markdown files kebab case.
        options: {
          caseType: 'kebab',
        },
        pattern: './**/*.md',
      },
    ],
  },
})

console.log(anotherReport)

CLI

Command: renami

Rename files using config. Searches for a config file if not provided, failing if none is found.

Usage:

renami [options]

| Option | Description | Type | Default | | ------------------- | --------------------------------------------------------------------------------------- | --------- | ------- | | --config-c | Path to config file. If not provided, a config file will be searched for automatically. | string | | | --verbose | Enable verbose logging. | boolean | false | | --help-h | Show help | boolean | | | --version-v | Show version number | boolean | |

Background

Implementation notes

Case sensitivity...

  • Will change case as requested, but doesn't allow identically named but differently-cased files.
  • scule

Template expansion...

Inline formatting syntax...

Number formatting...

Renaming...

Markdown body selection...

Style...

Similar projects

  • F2
    Great! But tricky to integrate in Obsidian because it's implemented in Go.
  • 75lb/renamer
    Close! Depends on Node. Written in JS instead of TS. No config file.
  • Name Mangler Notable in this context for metadata integration.
  • vidir
    Edit a directory of filenames in your text editor. Part of moreutils. Not exactly similar, but very useful.

Maintainers

kitschpatrol

License

MIT © Eric Mika