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

@kitsunekode/file-ops-cli

v1.0.0

Published

Interactive Bun-native file move, copy, and remove CLIs with shared safety-first workflows.

Readme

mvi / cpi / rmi

Interactive terminal file move, copy, and remove tools. Browse files visually, multi-select with fuzzy search, pick a destination folder when needed, and execute without typing paths.

mvi moves files. cpi copies files. rmi removes files by moving them to trash by default, with optional hard delete. The commands share the same source-selection interface and keybindings.

You can select both files and directories. Selections persist while you navigate, so you can collect items from multiple directories before choosing the destination.

Features

  • Visual file browser with directory navigation
  • Fuzzy search to filter files as you type
  • Multi-select with checkboxes
  • Nerd Font icons for 30+ file types
  • Destination folder picker (directories only)
  • Direct path jump for destination folders (for example ~/dotfiles)
  • Optional fzf-powered destination search from configurable roots
  • Bookmark and recent-destination support for faster targeting
  • Pre-flight validation (permissions, conflicts, circular moves)
  • Explicit confirmation before any operation (Y/n)
  • No-clobber by default — never overwrites without asking
  • Interactive remove command with trash-by-default safety
  • Verified copy pipeline for copy and cross-device move operations
  • Recovery journal for interrupted overwrite replacement
  • Lazy startup path for help/version and non-interactive failures
  • Bounded-concurrency directory metadata scanning for faster large-folder browsing
  • Clean terminal restore on exit, Ctrl+C, or crash
  • Zero runtime dependencies — just Bun

Requirements

  • Bun v1.0+
  • A terminal with Nerd Font for file icons (optional but recommended)

Install

bun add -g @kitsunekode/file-ops-cli

You can also install with npm if Bun is already available on your PATH at runtime:

npm install -g @kitsunekode/file-ops-cli

This installs mvi, cpi, and rmi globally.

Install From Source

git clone https://github.com/KitsuneKode/interactive-move-copy-cli.git
cd interactive-move-copy-cli
bun install
bun run link:global

Useful Scripts

bun run build           # bundle CLI entrypoints into dist/
bun run build:compile   # build standalone executables into dist/
bun run config:init     # create or normalize the shared config file
bun run config:edit     # open the shared config in $VISUAL / $EDITOR
bun run test            # run the full test suite
bun run check           # alias for the current verification suite
bun run pkg:check       # dry-run the published npm package contents
bun run changeset       # create a changeset entry for the next release
bun run version:packages # consume changesets and update release versions/changelogs
bun run link:global     # build and link mvi/cpi/rmi globally
bun run unlink:global   # remove the global link
bun run relink:global   # refresh the global link after changes
bun run clean           # remove dist/

If you use the globally linked commands while developing, rebuild and relink after behavior changes:

bun run build
bun run relink:global

Release Workflow

  • Add a changeset with bun run changeset for user-facing or release-worthy changes.
  • Work from short-lived dev branches and merge to main.
  • .github/workflows/version-packages.yml opens or updates a Version Packages PR from merged changesets.
  • Merging the version PR updates package.json and changelog entries for the next release.
  • CI runs on pull requests and on pushes to main, and its workflow summary records the exact package version it validated.
  • Release runs on pushes to main and manual dispatch. It publishes the exact package.json version of @kitsunekode/file-ops-cli through npm trusted publishing and creates a GitHub release tagged vX.Y.Z.
  • For a brand-new package, do one manual bootstrap publish first so the npm package settings page exists and you can attach the trusted publisher to release.yml.

Usage

mvi              # browse current directory, move selected files
mvi ~/Downloads  # start in ~/Downloads
cpi .            # copy files from current directory
rmi              # move selected files to trash
rmi --hard-delete .  # permanently delete selected files from current directory

Flags

-h, --help      Show help
-v, --version   Show version

rmi Delete Mode Flags

--trash         Move items to trash (default)
--hard-delete   Permanently delete items

rmi Config

All commands share one config file:

${XDG_CONFIG_HOME:-~/.config}/interactive-move-copy-cli/config.json

bun run link:global runs config:init first, so the file is created automatically if it does not exist yet. You can also run bun run config:init manually to create or normalize it, and bun run config:edit to open it in $VISUAL, then $EDITOR, then nano.

The repository ships an example at config.example.json.

Default config:

{
  "mvi": {},
  "cpi": {},
  "destinationSearch": {
    "roots": [
      "~"
    ],
    "bookmarks": {
      "dotfiles": "~/dotfiles",
      "projects": "~/Projects"
    },
    "rememberRecent": true,
    "recentLimit": 8
  },
  "rmi": {
    "mode": "trash"
  }
}

Current keys:

  • mvi: reserved for future move-specific settings
  • cpi: reserved for future copy-specific settings
  • destinationSearch.roots: directories searched by Ctrl+F, default ["~"]
  • destinationSearch.bookmarks: named directory aliases used by g
  • destinationSearch.rememberRecent: whether successful destinations are remembered automatically
  • destinationSearch.recentLimit: max number of recent destinations to keep
  • rmi.mode: default delete behavior, either "trash" or "hard-delete"

Recent destinations are stored separately in:

${XDG_CONFIG_HOME:-~/.config}/interactive-move-copy-cli/state.json

How It Works

Flow

mvi [dir]
  |
  v
File Browser (alt screen)
  - Browse files/dirs in current directory
  - Type to fuzzy-search, Space to select, Right to open dirs
  - Press Enter with selections to confirm source files/directories
  |
  v
Destination-Selection Loop:
  |
  v
Folder Picker (alt screen)
  - Browse directories only
  - Navigate into subdirs with Right
  - Press `g` to jump directly to a path or bookmark like `~/dotfiles` or `dotfiles`
  - Press `Ctrl+F` to jump to a destination with `fzf` across configured roots plus recent destinations
  - Embedded `fzf` uses a clean local config so user preview or bind overrides do not break selection
  - Cancelling `fzf` returns to the folder picker with a notice
  - Press Enter or 'c' to confirm current directory as destination
  |
  v
Validation (pre-flight checks)
  - Destination exists and is writable?
  - Source != destination?
  - Not moving a parent into its own child?
  - Name conflicts at destination? -> prompt overwrite/skip/abort
  - Esc at conflict prompt goes back to Folder Picker
  |
  v
Confirmation Prompt
  - Lists all files and destination
  - Y or Enter confirms, n/N/Ctrl+C aborts
  - Esc goes back to Folder Picker to reselect the destination
  |
  v
Execution
  - Uses atomic rename for same-device moves when possible
  - Uses hidden staging paths plus verification for copies and fallback moves
  - Shows per-file progress with checkmarks/crosses
  - Prints summary: N moved/copied, M failed
rmi [dir]
  |
  v
File Browser (alt screen)
  - Browse files/dirs in current directory
  - Type to fuzzy-search, Space to select, Right to open dirs
  - Press Enter with selections to confirm source files/directories
  |
  v
Validation (pre-flight checks)
  - Selected sources still exist?
  - Not deleting a parent and child in the same run?
  |
  v
Confirmation Prompt
  - Trash by default, hard delete only when explicitly requested
  - Y or Enter confirms, n/N/Ctrl+C/Esc aborts
  |
  v
Execution
  - Same-device trash uses rename into the trash store when possible
  - Cross-device trash uses verified copy-then-delete
  - Hard delete permanently removes the selected paths

Keybindings — Source Selection

| Key | Action | | --- | --- | | Up/Down | Navigate file list | | Left | Go to parent / delete search char | | Right | Open directory | | Space | Toggle selection on current file | | Enter | Confirm selection | | Backspace | Delete search char / go to parent | | Ctrl+A | Select all visible files | | Ctrl+D | Deselect all | | Ctrl+R | Reset to the starting directory/state | | Tab | Show selected files summary | | Esc | Clear search / quit | | Any letter | Fuzzy search |

Keybindings — Destination Picker

| Key | Action | | --- | --- | | Up/Down | Navigate directories | | Left | Go to parent | | Right | Open directory | | g | Jump to a destination by path or bookmark | | Ctrl+F | Jump to a destination with fzf | | Enter | Confirm current directory | | Backspace | Go to parent | | c | Confirm current directory | | Ctrl+R | Reset to the starting directory | | Esc | Cancel and abort |

Data Safety

This tool is designed to avoid silent data loss:

  1. No-clobber default — If a path with the same name exists at the destination, the operation is skipped unless you explicitly choose overwrite.

  2. Explicit confirmation — A final [Y/n] prompt shows exactly what will happen before any operation runs. Ctrl+C always aborts. For mvi/cpi, Esc goes back to the destination picker so you can reselect without losing your file selection.

  3. Verified staging — Copies and cross-device moves write into a hidden staging path, verify the staged result against the source, and only then promote it to the final destination.

  4. Safe replacement — Overwrite mode replaces the existing destination path instead of merging or nesting directories.

  5. Conflict detection — Before execution, the validator checks destination conflicts, duplicate destination names within the selection, and parent/child overlaps that would cause partial or ambiguous results.

  6. Recovery journal — Interrupted overwrite replacements leave a journal so the next run can restore the previous destination if the final path was never promoted.

  7. Verified delete on move fallback — When a move cannot use an atomic same-device rename, the source is deleted only after the destination copy has been verified.

  8. Trash by defaultrmi moves items to trash unless you explicitly opt into --hard-delete or set that mode in config.

  9. Clean exit — Terminal state (raw mode, alternate screen, cursor visibility) is restored on normal exit, Ctrl+C, SIGINT, and SIGTERM.

Performance Notes

  • Cold paths stay cheap. --help, --version, and non-TTY exits avoid loading the full TUI/execution stack.
  • Directory scans use bounded concurrency instead of serial lstat() calls.
  • The file browser reuses cached directory listings and memoized search results instead of recomputing on every cursor move.
  • Safety-critical copy verification is intentionally not optimized away.

Destination Search Notes

  • g is the exact jump path: use it for absolute paths, relative paths, ~/..., or configured bookmark names.
  • Ctrl+F is the fuzzy destination path: it searches configured roots plus recent destinations with fzf.
  • Embedded fzf receives plain absolute directory paths only.
  • Embedded fzf intentionally ignores global preview and bind overrides so the destination picker stays stable across different user setups.
  • After g or Ctrl+F, the picker jumps to the chosen directory and stays open. Press Enter or c to confirm it.

Shell Completions

The commands ship completion files, but they are not installed automatically. The examples below assume you are working from a cloned checkout or unpacked package contents.

Bash

Add to ~/.bashrc:

source /path/to/interactive-move-copy-cli/completions/mvi.bash

Zsh

Source the helper file directly from ~/.zshrc:

[[ ! -f ~/.config/zsh/mvi.zsh ]] || source ~/.config/zsh/mvi.zsh

One-time setup:

mkdir -p ~/.config/zsh
cp /path/to/interactive-move-copy-cli/completions/mvi.zsh ~/.config/zsh/mvi.zsh

The TUI requires an interactive terminal. --help and --version work in non-interactive shells, but browsing mode does not.

Project Structure

src/
  bin/mvi.ts, cpi.ts, rmi.ts      Entry points (shebang scripts)
  config.ts               Shared config loading and normalization
  cli.ts                   Arg parsing, orchestration
  core/types.ts            TypeScript interfaces
  core/constants.ts        ANSI codes, key maps, colors
  tui/terminal.ts          Raw mode, keypress parsing, cleanup
  tui/renderer.ts          Diff-based ANSI screen rendering
  tui/fuzzy.ts             Fuzzy match with scoring
  tui/file-browser.ts      Source file selection component
  tui/folder-picker.ts     Destination directory picker
  fs/file-info.ts          Directory listing with caching
  fs/icons.ts              Nerd Font icon mapping
  fs/format.ts             Size/date formatting
  ops/validator.ts         Pre-flight validation
  ops/executor.ts          Verified execution with progress
  ops/safe-fs.ts           Staging, verification, and recovery journal logic
  scripts/config-init.ts   Create or normalize the default config
  scripts/config-edit.ts   Open the config in the default editor
tests/
  cli.test.ts              Non-interactive runtime checks
  fuzzy.test.ts            Fuzzy matching tests
  format.test.ts           Formatting tests
  icons.test.ts            Icon resolution tests
  recovery.test.ts         Journal recovery behavior
  config.test.ts           Config loading tests
  validator.test.ts        Validation logic tests

Testing

bun test

License

MIT